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

Последнее обновление: 09.03.2023

По умолчанию компилятор при компиляции классов генерирует специальный конструктор - конструктор копирования, который позволяет создать объект на основе другого объекта (по сути копирует объект). Конструктор копирования по умолчанию копирует значения полей объекта, в новый объект. Рассотрим простейший пример:

#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};    // конструктор копирования отсутствует
}
Дополнительные материалы
Помощь сайту
Юмани:
410011174743222
Перевод на карту
Номер карты:
4048415020898850