Конструктор копирования

В случае присваивания одного объекта другому при объявлении, а также при передаче объекта в качестве параметра в функцию и возврате объекта из функции вызывается особый конструктор — конструктор копирования. Если конструктор копирования не определен, то используется конструктор копирования по умолчанию. В этом случае создается побитовая копия объекта. Если внутри класса производится динамическое выделение памяти, то обязательно следует определить конструктор копирования явным образом, так как создание побитовой копии в этом случае недопустимо. Конструктор копирования имеет следующий формат:

<Название класса>(const <Название класса> &<Название объекта>);

Название конструктора копирования совпадает с названием класса. В качестве параметра конструктор принимает константную ссылку на копируемый объект. Тип возвращаемого значения не указывается. Продемонстрируем использование конструктора копирования на примере (листинг 13.9).

Листинг 13.9. Конструктор копирования

#include <iostream>

class C {
   int x_, y_;
public:
   C(int x, int y): x_(x), y_(y) { }   // Обычный конструктор
   C(const C &obj)
      : x_(obj.x_), y_(obj.y_) {       // Конструктор копирования
      std::cout << "copy" << std::endl;
   }
   ~C() {                              // Деструктор
      std::cout << "~C()" << std::endl;
   }
   void setX(int x) { x_ = x; }
   void dump() {
      std::cout << x_ << ' ' << y_ << std::endl;
   }
};
void func1(C obj) { } // Передача объекта через параметр
C func2(int x) {
   return C(x, 10);   // Возврат объекта из функции (RVO)
}
C func3(int x) {
   C obj(x, 11);
   return obj;        // Возврат объекта из функции (NRVO)
}

int main() {
   C obj1(40, 30);
   C obj2 = obj1;     // Вызывается конструктор копирования
   obj2.dump();       // 40 30
   func1(obj1);       // Вызывается конструктор копирования
   obj2 = obj1;       // Конструкторы не вызываются!!!
                      // Требуется перегрузка оператора =
                      // По умолчанию побитовая копия
   obj2.setX(5);
   obj1.dump();       // 40 30
   obj2.dump();       // 5 30
   // Возврат объекта из функции
   C obj3 = func2(10);
   obj3.dump();       // 10 10
   C obj4 = func3(20);
   obj4.dump();       // 20 11
   return 0;
}

Обратите внимание на то, что при простом присваивании объектов (вне объявления) конструктор копирования не вызывается. В этом случае нужно перегружать оператор присваивания. По умолчанию создается побитовая копия объекта.

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

  • RVO (return value optimization) — объект создается в инструкции return (например, как внутри функции func2() из листинга 13.9);
  • NRVO (named return value optimization) — объект создается внутри функции и возвращается инструкцией return (например, как внутри функции func3() из листинга 13.9).

Некоторые компиляторы (особенно старые) не выполняют оптимизацию (в частности при варианте NRVO) и вызывают конструктор копирования. В проекте Test64 оптимизация выполняется при обоих вариантах, поэтому конструктор копирования при возврате объекта из функции не вызывается.

Учебник C++ (MinGW-W64)
Учебник C++ (MinGW-W64) в формате PDF

Помощь сайту

ЮMoney (Yandex-деньги): 410011140483022

ПАО Сбербанк:
Счет: 40817810855006152256
Реквизиты банка:
Наименование: СЕВЕРО-ЗАПАДНЫЙ БАНК ПАО СБЕРБАНК
Корреспондентский счет: 30101810500000000653
БИК: 044030653
КПП: 784243001
ОКПО: 09171401
ОКОНХ: 96130
Скриншот реквизитов