Статья: Классы: копирование и присваивание
if(this == &rhs) return *this; // проверка на присваивание себе
else { X=rhs.X; Y=rhs.Y; } //то, что делает оператор полезного
return *this; // возврат ссылки на объект
}
Сейчас мы попробуем в ней разобраться, благо для всех операций присваивания проверка на присваивание себе со-вершенно одинакова. Оператор if(this == &rhs) проверяет, не совпадает ли аргумент с самим объектом. Указатель this со-держит адрес вызывающего объекта, &rhs читается как "адрес rhs".
Таким образом, сравниваются два адреса. Если они эквивалентны (==), то это один и тот же объект. В этом случае, в полном соответствии с требованием воз-врата ссылки на объект, просто возвращаем *this (заметьте, что в конце функции делается то же самое) и выходим из функции.
Вспомните, что this - это указатель. Значение указателя - это адрес. Чтобы получить значение указателя, его следует разыменовывать. Разыменование указателя выглядит так: *ptr. Указатель this разыменовывается точно так же: *this.
Помещая эти две строки в начале и в конце тела операции присваивания, мы уменьшаем вероятность возникновения утечек памяти из-за этой опера-ции. Если вы запомнили приведенный здесь синтаксис копирования и при-сваивания и, определяя новый класс, сразу будете определять и их тоже, то это уже полдела.
Зачем C++ требует определения этих функций-членов?
Язык C++ не слишком сильно ограничивает свободу программистов в методах разработки программного обеспечения. В частности, он не навязывает вам способы копирования и присваивания. Количество и разнообразие ситуаций, в которых происходит копирование объектов, удивительно велико. Для очень простых объектов, состоящих из одного-двух элементов, затраты на копирование незначительны, но для более сложных, таких как графический интерфейс пользователя или комплексные типы данных, оперирующие с динамической памятью, издержки на копирование существенно возрастают.
Во-первых, имейте в виду, что если вы не определите для нового класса конструктор копий, то C++ создаст его сам. Причина заключается в том, что компилятору самому может потребоваться возможность создания копий, значит, эти две функции обязаны быть определены.
Во-вторых, вам может потребоваться заблокировать копирование, либо вести подсчет ссылок, или еще что-нибудь. Если вы не создадите эти функции, то C++ создаст для них версии по умолчанию.
Создаваемые компилятором версии обеих этих функций не всегда будут вас удовлетворять. Версии компилятора выполняют буквальное, или поразряд-ное, копирование. В некоторых случаях это неразумно. Не пожалейте времени на изучение ситуаций, которые могут вам встретиться при разработке программ.
Вот лишь некоторые из бесчисленного множества возможных ситуаций, в которых происходит копирование:
POINT х;
POINT у(х); // Прямой вызов конструктора копий.
POINT х = у; // Выглядит как присваивание, но на самом деле
// вызывает конструктор копий. Почему? См. ниже.
POINT a, b;
a = b; // Вызов операции присваивания
POINT Foo(); // Возврат по значению, вызывает копирование
void Foo(POINT); // Передача по значению, создает копию
Во всех этих случаях выполняется копирование. В ходе выполняемой компилятором оптимизации могут появиться и другие варианты. Это та область, где знание действительно сила, способная помочь вам избежать утечек памяти.
В операторе типа POINT х = у; не вызывается операция присваивания класса POINT, хотя на первый взгляд выглядит это именно так. Причина состоит в том, что операция присваивания - это функция-член, а значит может быть вызвана только для уже существующих объектов, в то время как в этом фрагменте происходит создание нового объекта х.
Если объект создается в той же строке, в которой он выступает в качестве левостороннего аргумента, то вызывается конструктор. Строка
Х х = у; // вызов конструктора копий
эквивалентна строке
Х х(у); // вызов конструктора копий
БИКЮ , что совсем не то же самое, что