По умолчанию компилятор при компиляции классов генерирует специальный конструктор - конструктор копирования, который позволяет создать объект на основе другого объекта (по сути копирует объект). Конструктор копирования по умолчанию копирует значения полей объекта, в новый объект. Рассотрим простейший пример:
#include <iostream> class Person { private: std::string name; unsigned age; public: Person(std::string p_name, unsigned p_age) { name = p_name; age = p_age; } void print() { std::cout << "Name: " << name << "\tAge: " << age << std::endl; } }; int main() { Person tom{"Tom", 38}; Person tomas{tom}; // создаем объект tomas на основе объекта tom tomas.print(); // Name: Tom Age: 38 }
В данном случае строка:
Person tomas{tom};
представляет вызов конструктора копирования. Хотя мы нигде в коде не определяем конструктор, который принимал бы другой объект Person - подобный конструктор автоматически генерирует компилятор. В итоге объект tomas будет иметь все те же значения, что и объект tom.
Конструктор копирования - замечательная вещь, когда нам надо создать один объект на основе другого, однако данный конструктор имеет свои недостатки. Например, если поле представляет указатель, то копируется адрес. В итоге поля обоих объектов будут указывать на один и тот же адрес в памяти. Соответственно если мы захотим изменить значение для одного объекта, это значение также изменится и для другого объекта. И в этом случае мы можем определить свой конструктор копирования.
Конструктор копирования должен принимать в качестве параметра объект того же класса. Причем параметр лучше принимать по ссылке, потому что при передаче по значению компилятор будет создавать копию объекта. А для создания копия объекта будет вызываться конструктор копирования, что приведет бесконечной рекурсии. Итак, определим свой конструктор копирования:
#include <iostream> class Person { std::string name; unsigned age; public: Person(std::string p_name, unsigned p_age) { name = p_name; age = p_age; } Person(const Person &p) { name = p.name; age = p.age + 1; // для примера } void print() { std::cout << "Name: " << name << "\tAge: " << age << std::endl; } }; int main() { Person tom{"Tom", 38}; Person tomas{tom}; // создаем объект tomas на основе объекта tom tomas.print(); // Name: Tom Age: 39 }
Итак, здесь конструктор копирования принимает константную ссылку на объект Person и присваивает значения его полей соответствующим полям текщего объекта. Для примера, чтобы данные чуть отличались, я добавил к свойству age единицу. В итоге вместо конструктора копирования по умолчанию будет применяться кастомный конструктор.
Конструктор копирования не всегда может быть нужен. И его можно удалить с помощью оператора delete:
#include <iostream> class Person { std::string name; unsigned age; public: Person(std::string p_name, unsigned p_age) { name = p_name; age = p_age; } Person(const Person &p) = delete; // удаляем конструктор void print() { std::cout << "Name: " << name << "\tAge: " << age << std::endl; } }; int main() { Person tom{"Tom", 38}; //Person tomas{tom}; // конструктор копирования отсутствует }