Главная->Інформатика та програмування->Содержание->Перегрузка оператора присваивания

ОБЪЕКТНО-ОРИЕНТИРОВАННОЕ ПРОГРАММИРОВАНИЕ В C++ (4-Е ИЗДАНИЕ) (часть 11) онлайн

Перегрузка оператора присваивания

Следующий коротенький пример демонстрирует технологию перегрузки опера-

тора присваивания.

Листинг 11.16. Программа ASSIGN

// assign.cpp

// перегрузка операции присваивания (=)

#include <iostream>

using namespace std;

///////////////////////////////////////////////////////////

class alpha

  {

  private:

    int data;

  public:

    alpha()                     //конструктор без аргументов

      { }

    alpha(int d)                //конструктор с одним аргументом

      { data = d; }

    void display()              //вывести данные

      { cout << data; }

    alpha operator = (alpha& a) //перегружаемый =

      {

      data = a.data;            //не выполняется автоматически

      cout << "\nЗапущен оператор присваивания";

      return alpha(data);       //возвращает копию alpha

      }

  };

///////////////////////////////////////////////////////////

 

 

Листинг 11.16 (продолжение)

int main()

{

  alpha a1(37);

  alpha a2;

 

  a2 = a1;                       //запуск перегружаемого =

  cout << "\na2="; a2.display(); //вывести a2

 

  alpha a3 = a2;                 //НЕ запускается =

  cout << "\na3="; a3.display(); //вывести a3

  cout << endl;

  return 0;

}

Класс alpha очень прост, в нем содержится только один элемент данных.

Конструкторы инициализируют данные, а методы выводят их значения на экран.

Вот и все, что тут делается. А новизна примера ASSIGN заключается в функции

operator=(), перегрузившей оператор = operator.

В main() мы определяем переменную a1 и присваиваем ей значение 37, опре-

деляем переменную а2, но значения ей не присваиваем. Затем, как видите, при-

сваиваем а2 значение a1:

а2 = a1;                //Выражение присваивания

Тем самым мы запускаем нашу перегружаемую функцию operator=(). Про-

грамма ASSIGN заканчивается с таким результатом:

Запущен оператор присваивания

а2=37

а3=37

Инициализация — это не присваивание

В последних двух строчках кода ASSIGN мы инициализируем объект аЗ объектом

а2 и выводим его значение на экран. И пусть вас не смущает синтаксис в приве-

денном ниже выражении. Выражению

alpha аЗ = а2; //инициализация копирования. а не

               //присваивание!

соответствует не присваивание, а инициализация. А эффект будет при этом тот

же, что при использовании выражения

alpha аЗ(а2); //альтернативный вариант инициализации

              //копирования

Именно поэтому оператор присваивания выполняется только один раз, что

и отражено в результатах работы программы в единственной строчке вызова:

Запущен оператор присваивания

Ответственность

Когда перегружается оператор присваивания, подразумевается, что за все, что по

умолчанию делал этот оператор, отвечает программист. Часто это связано с ко-

пированием элементов данных из одного объекта в другой. Класс alpha из про-

 

граммы ASSIGN включает в себя только один элемент — data, поэтому функция

operator=() копирует его значение с помощью следующего выражения:

data = a.data;

В обязанности этой функции также входит вывод на экран строки «Запуще-

на операция присваивания», по которой мы можем определить, что функция

выполняется.

Передача по ссылке

Аргумент для функции operator=() передается по ссылке. Нельзя сказать, что это

совершенно необходимо, но обычно именно так и делается. Зачем? Как вы знаете,

аргумент, передающийся по значению, создает в памяти собственную копию для

функции, которой он передается. Аргумент, передающийся функции operator=(),

не является исключением. А если объекты большие? Их копии могут занять в па-

мяти очень много места, причем от них нет никакой пользы. Когда же значения

аргументов передаются по ссылке, копии не создаются, что помогает экономить

память.

Есть и еще одна причина. В определенных ситуациях необходимо отслежи-

вать количество созданных объектов (как в примере STATFUNC, где мы нумерова-

ли созданные объекты). Если же компилятор генерирует еще какие-то объекты

всякий раз, когда используется оператор присваивания, то есть шанс сильно за-

высить оценку их количества. С помощью передачи по ссылке удается ликвиди-

ровать прецеденты ложного создания объектов.

Возврат значений

Как вы уже успели заметить, функция может возвращать результат в вызыва-

ющую программу по значению или по ссылке. При возвращении по значению

происходит фактически передача результата, а значит, создается копия объекта,

которая и возвращается в программу. В вызывающей программе этот вновь соз-

данный объект может присваиваться какому-либо другому объекту или исполь-

зоваться как-то еще, это уже не так важно. Если же возвращение происходит по

ссылке, никакой новый объект не создается. Ссылка на исходный объект — вот

и все, что возвращается в качестве результата.

Функция operator=() возвращает результат путем создания временного объекта

alpha и его инициализации с помощью одноаргументного конструктора в выра-

жении

return alpha(data);

Возвращаемое значение — это копия исходного, а не тот же самый объект,

чьим методом является перегружаемая операция =. Такой стиль возвращения

значений позволяет выстраивать эти функции в цепочку:

аЗ = а2 = a1;

Да, впечатляет, но все равно возвращение результата по значению имеет все

те же недостатки, что и передача по значению. Прежде всего, это создание в па-

мяти копии возвращаемого объекта, что может привести к некоторой путанице.

 

А можем мы вернуть результат по ссылке, используя описатель, показанный ни-

же, для перегружаемого присваивания?

alpha& operator = (аlpha& а) //Неудачная мысль

К сожалению, нет, мы не можем осуществить возврат результата по ссылке

при работе с локальными переменными данной функции.

Напомним, что локальные переменные, то есть созданные внутри функции и

не объявленные статическими, уничтожаются при выходе из функции. Но, как

мы знаем, возвращение по ссылке передает в вызывающую программу только

адрес данных, который для локальных переменных указывает на данные, находя-

щиеся внутри функции. При возврате из функции указатель хранит уже какое-

то не имеющее смысла значение. Компилятор может сигнализировать о такой

ситуации предупреждением. В разделе «Указатель this» этой главы мы пока-

жем, как можно решить эту проблему.

Ограничение на наследование

Оператор присваивания уникален среди остальных операторов тем, что он не

может наследоваться. Перегрузив присваивание в базовом классе, вы не сможете

использовать ту же функцию в порожденных классах.

 

21