
- •1 4 Семинар 1. Классы
- •Что принесло с собой ооп
- •1 6 Семинар 1. Классы
- •От структуры — к классу
- •Задача 1.1. Поиск в массиве структур
- •1 8 Семинар 1. Классы
- •Инициализаторы конструктора
- •28 Семинар 1. Классы
- •Конструктор копирования
- •Перегрузка операций инкремента
- •Задача 1.1. Поиск в массиве структур 31
- •1 Является переменной встроенного типа, форма инкремента безразлична:
- •Статические элементы класса
- •3 4 Семинар 1. Классы
- •4 0 Семинар 1. Классы
- •Задача 1.2. Реализация класса треугольников 45
Перегрузка операций инкремента
Операция инкремента имеет две формы: префиксную и постфиксную. Для первой
формы сначала изменяется состояние объекта в соответствии с данной операцией,
а затем он (объект) используется в том или ином выражении. Для второй
формы объект испо.тьзуется в том состоянии, которое у него было до начала
операции, а потом уже его состояние изменяется.
Чтобы компилятор смог различить эти две формы операции инкремента, для
них используются разные сигнатуры, например:
Po1nt& operator ++(): // префиксный инкремент
Point o^^er^tor ++(1nt); // постфиксный инкремент
Покажем реализацию данных операций на примере класса Point:
Po1nt& Point::operator ++() {
X++ ; y+4-;
return 4h1s:
}
Point Point::operator ++(1nt) {
Point old = 4h1s;
X++; У++;
return old:
}
Обратите внимание, что в префиксной операции осуществляется возврат результата
по ссылке. Это предотвращает вызов конструктора копирования для создания
возвращаемого значения и последующего вызова деструктора. В постфиксной
операции инкремента возврат по ссылке не подходит, поскольку необходимо
вернуть первоначальное состояние объекта, сохраненное в локальной перемен-
Задача 1.1. Поиск в массиве структур 31
ной old. Таким образом, префиксный инкремент является более эффективной
операцией, чем постфиксный инкремент.
Заметим, что ранее мы не обращали внимания на запись инкремента в заголовке
цикла for. Так, в первой книге практикума во всех примерах использовалась
постфиксная форма инкремента: for (1 = 0: 1 < п; 1++). Дело в том, что пока параметр
1 Является переменной встроенного типа, форма инкремента безразлична:
программа будет работать одинаково. Ситуация меняется, если параметр 1
есть объект некоторого класса — в этом случае префиксная форма инкремента
оказывается более эффективной. Когда мы будем использовать контейнерные
классы стандартной библиотеки, параметр 1 в заголовке цикла очень часто будет
представлять объект-итератор.
СОВЕТ
Всегда используйте префиксный инкремент для параметра цикла for — это дает более
эффективный программный код.
ПРИМЕЧАНИЕ
Все сказанное о данной операции относится также и к операции декремента.
Перегрузка операции присваивания
о перегрузке этой операции следует поговорить особо по нескольким причинам.
Во-первых, если вы не определите эту операцию в некотором классе, то компилятор
создаст операцию присваивания по умолчанию, которая выполняет поэлементное
копирование объекта. В этом случае возможно появление тех же проблем,
которые возникают при использовании конструктора копирования по
умолчанию (см. выше). Поэтому запомните золотое правило: если в классе требуется
определить конструктор копирования, то его верной спутницей должна
быть перегруженная операция присваивания, и наоборот.
Во-вторых, операция присваивания может быть определена только в форме метода
класса.
В-третьих, операция присваивания не наследуется (в отличие от всех остальных
операций).
Например, для класса Man из задачи 1,1 перегрузку операции присваивания можно
определить следующим образом:
// Man.h (интерфейс класса)
class Man {
public:
// . . .
Man& operator =(const Man&): // операция присваивания
private:
char* pName:
// . . .
}:
3 2 Семинар 1. Классы
// Мап.срр (реализация класса)
// . . .
Мап8( Man: :operator =(const Man& man) {
if (this == &man) return 4h1s; // проверка на самоприсваивание
delete [] pName: // уничтожить предыдущее значение
pName = new char[strlen(man.pName) + 1];
strcpyCpName. man.pName);
birth_year = man.b1rth_year:
pay = man.pay;
return 4 h i s:
}
Обратите внимание на несколько простых, но важных моментов при реализации
операции присваивания:
• убедитесь, что не выполняется присваивание вида х = х. Если левая и правая
части ссылаются на один и тот же объект, то делать ничего не надо. Если не
перехватить этот особый случай, то следующий шаг уничтожит значение, на
которое указывает pName, еще до того, как оно будет скопировано;
• удалите предыдущие значения полей в динамически выделенной памяти;
• выделите память под новые значения полей;
• скопируйте в нее новые значения всех полей;
• возвратите значение объекта, на которое указывает this (то есть 4h1s).