Читаем Программирование. Принципы и практика использования C++ Исправленное издание полностью

Можно не соглашаться с тем, что инкрементация перечисления Month является широко распространенным способом, заслуживающим реализации в виде отдельного оператора. Однако что вы скажете об операторе вывода? Его можно описать так:


vector month_tbl;

ostream& operator<<(ostream& os, Month m)

{

  return os << month_tbl[m];

}


Это значит, что объект month_tbl был инициализирован где-то, так что, например, month_tbl[Mar] представляет собой строку "March" или какое-то другое подходящее название месяца (см. раздел 10.11.3).

Разрабатывая собственный тип, можно перегрузить практически любой оператор, предусмотренный в языке С++, например +, , *, /, %, [], , ^, !, &, <, <=, > и >=. Невозможно определить свой собственный оператор; можно себе представить, что программист захочет иметь операторы ** или $=, но язык С++ этого не допускает. Операторы можно определить только для установленного количества операндов; например, можно определить унарный оператор , но невозможно перегрузить как унарный оператор <= (“меньше или равно”). Аналогично можно перегрузить бинарный оператор +, но нельзя перегрузить оператор ! (“нет”) как бинарный. Итак, язык позволяет использовать для определенных программистом типов существующие синтаксические выражения, но не позволяет расширять этот синтаксис.

Перегруженный оператор должен иметь хотя бы один операнд, имеющий тип, определенный пользователем.


int operator+(int,int); // ошибка: нельзя перегрузить встроенный

                        // оператор +

Vector operator+(const Vector&, const Vector &); // OK

Vector operator+=(const Vector&, int);           // OK


  Мы рекомендуем не определять оператор для типа, если вы не уверены полностью, что это значительно улучшит ваш код. Кроме того, операторы следует определять, сохраняя их общепринятый смысл: оператор + должен обозначать сложение; бинарный оператор * — умножение; оператор [] — доступ; оператор — вызов функции и т.д. Это просто совет, а не правило языка, но это хороший совет: общепринятое использование операторов, такое как символ + для сложения, значительно облегчает понимание программы. Помимо всего прочего, этот совет является результатом сотен лет опыта использования математических обозначений.

Малопонятные операторы и необычное использование операторов могут запутать программу и стать источником ошибок. Более на эту тему мы распространяться не будем. Просто в следующих главах применим перегрузку операторов в соответствующих местах.

Интересно, что чаще всего для перегрузки выбирают не операторы +, , *, и /, как можно было бы предположить, а =, ==, !=, <, [] и .

9.7. Интерфейсы классов

  Ранее мы уже указывали, что открытый интерфейс и реализация класса должны быть отделены друг от друга. Поскольку в языке С++ остается возможность использовать простые структуры struct, некоторые профессионалы могут не согласиться с этим утверждением. Однако как разработать хороший интерфейс? Чем хороший интерфейс отличается от плохого? Частично на эти вопросы можно ответить только с помощью примеров, но существует несколько общих принципов, которые поддерживаются в языке С++.

• Интерфейс должен быть полным.

• Интерфейс должен быть минимальным.

• Класс должен иметь конструкторы.

• Класс доложен поддерживать копирование (или явно запрещать его) (см. раздел 14.2.4).

• Следует предусмотреть тщательную проверку типов аргументов.

• Необходимо идентифицировать немодифицирующие функции-члены (см. раздел 9.7.4).

• Деструктор должен освобождать все ресурсы (см. раздел 17.5). См. также раздел 5.5, в котором описано, как выявлять ошибки и сообщать о них на этапе выполнения программы.


Первые два принципа можно подытожить так: “Интерфейс должен быть как можно более маленьким, но не меньше необходимого”. Интерфейс должен быть маленьким, потому что его легче изучить и запомнить, а программист, занимающийся реализацией класса, не будет терять время на реализацию излишних или редко используемых функций. Кроме того, небольшой интерфейс означает, что если что-то пойдет не так, как задумано, для поиска причины потребуется проверить лишь несколько функций. В среднем чем больше открытых функций, тем труднее найти ошибку, — пожалуйста, не усложняйте себе жизнь, создавая классы с открытыми данными. Но, разумеется, интерфейс должен быть полным, в противном случае он будет бесполезным. Нам не нужен интерфейс, который не позволяет нам делать то, что действительно необходимо.

Перейдем к изучению менее абстрактных и более реалистичных понятий, поддерживаемых в языке С++. 

9.7.1. Типы аргументов

Определяя конструктор класса Date в разделе 9.4.3, мы использовали в качестве аргументов три переменные типа int. Это породило несколько проблем.


Перейти на страницу:
Нет соединения с сервером, попробуйте зайти чуть позже