Чтение онлайн

на главную

Жанры

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

Month operator++(Month& m) // префиксный инкрементный оператор

{

m = (m==Dec) ? Jan : Month(m+1); // "циклический переход"

return m;

}

Конструкция

? :
представляет собой арифметический оператор “если”: переменная
m
становится равной
Jan
, если (
m==Dec
), и
Month(m+1)
в противном случае. Это довольно элегантный способ, отражающий цикличность календаря. Тип
Month
теперь можно
написать следующим образом:

Month m = Sep;

++m; // m становится равным Oct

++m; // m становится равным Nov

++m; // m становится равным Dec

++m; // m становится равным Jan ("циклический переход")

Можно не соглашаться с тем, что инкрементация перечисления

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

vector<string> 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
. Это породило несколько проблем.

Date d1(4,5,2005); // Ой! Год 4, день 2005

Date d2(2005,4,5); // 5 апреля или 4 мая?

Первая проблема (недопустимый день месяца) легко решается путем проверки в конструкторе. Однако вторую проблему (путаницу между месяцем и днем месяца) невозможно выявить с помощью кода, написанного пользователем. Она возникает из-за того, что существуют разные соглашения о записи дат; например, 4/5 в США означает 5 апреля, а в Англии — 4 мая. Поскольку эту проблему невозможно устранить с помощью вычислений, мы должны придумать что-то еще. Очевидно, следует использовать систему типов.

Поделиться:
Популярные книги

Черный Маг Императора 13

Герда Александр
13. Черный маг императора
Фантастика:
попаданцы
аниме
сказочная фантастика
фэнтези
5.00
рейтинг книги
Черный Маг Императора 13

Последняя Арена 4

Греков Сергей
4. Последняя Арена
Фантастика:
рпг
постапокалипсис
5.00
рейтинг книги
Последняя Арена 4

Маяк надежды

Кас Маркус
5. Артефактор
Фантастика:
городское фэнтези
попаданцы
аниме
5.00
рейтинг книги
Маяк надежды

Великий перелом

Ланцов Михаил Алексеевич
2. Фрунзе
Фантастика:
попаданцы
альтернативная история
5.00
рейтинг книги
Великий перелом

Сопротивляйся мне

Вечная Ольга
3. Порочная власть
Любовные романы:
современные любовные романы
эро литература
6.00
рейтинг книги
Сопротивляйся мне

Инквизитор Тьмы 2

Шмаков Алексей Семенович
2. Инквизитор Тьмы
Фантастика:
попаданцы
альтернативная история
аниме
5.00
рейтинг книги
Инквизитор Тьмы 2

Мастер Разума V

Кронос Александр
5. Мастер Разума
Фантастика:
городское фэнтези
попаданцы
5.00
рейтинг книги
Мастер Разума V

Бандит 2

Щепетнов Евгений Владимирович
2. Петр Синельников
Фантастика:
боевая фантастика
5.73
рейтинг книги
Бандит 2

Истребители. Трилогия

Поселягин Владимир Геннадьевич
Фантастика:
альтернативная история
7.30
рейтинг книги
Истребители. Трилогия

Гардемарин Ее Величества. Инкарнация

Уленгов Юрий
1. Гардемарин ее величества
Фантастика:
городское фэнтези
попаданцы
альтернативная история
аниме
фантастика: прочее
5.00
рейтинг книги
Гардемарин Ее Величества. Инкарнация

Падение Твердыни

Распопов Дмитрий Викторович
6. Венецианский купец
Фантастика:
попаданцы
альтернативная история
5.33
рейтинг книги
Падение Твердыни

"Дальние горизонты. Дух". Компиляция. Книги 1-25

Усманов Хайдарали
Собрание сочинений
Фантастика:
фэнтези
боевая фантастика
попаданцы
5.00
рейтинг книги
Дальние горизонты. Дух. Компиляция. Книги 1-25

Ох уж этот Мин Джин Хо 2

Кронос Александр
2. Мин Джин Хо
Фантастика:
попаданцы
5.00
рейтинг книги
Ох уж этот Мин Джин Хо 2

Энфис 6

Кронос Александр
6. Эрра
Фантастика:
героическая фантастика
рпг
аниме
5.00
рейтинг книги
Энфис 6