Menu
Coddy logo textTech

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

Часть раздела Объектно-ориентированное программирование путешествия по C++ на Coddy. Урок 20 из 104.

Конструктор copy constructor создаёт новый объект как копию существующего объекта. Он called, когда ты инициализируешь объект на основе another объекта того же типа, передаёшь объект по значению или возвращаешь объект по значению.

Конструктор copy принимает ссылку const на объект того же class:

class Player {
    std::string name;
    int health;
public:
    Player(std::string n, int h) : name(n), health(h) {}
    
    // Конструктор копирования
    Player(const Player& other) {
        name = other.name;
        health = other.health;
    }
};

Player original("Hero", 100);
Player copy = original;    // Вызван конструктор копирования
Player another(original);  // Также вызывает конструктор копирования

Если вы не define copy constructor, компилятор сгенерирует такой, который выполняет неглубокое копирование — он напрямую копирует значение каждого member. Это прекрасно работает для простых типов, но вызывает проблемы, когда ваш class управляет динамической памятью.

class Buffer {
    int* data;
    size_t size;
public:
    Buffer(size_t s) : size(s) {
        data = new int[size];
    }
    
    // Конструктор глубокого копирования
    Buffer(const Buffer& other) : size(other.size) {
        data = new int[size];              // Выделить новую память
        for (size_t i = 0; i < size; i++)
            data[i] = other.data[i];       // Скопировать содержимое
    }
    
    ~Buffer() { delete[] data; }
};

Без пользовательского конструктора copy оба объекта указывали бы на одну и ту же область памяти. Когда один из них destroyed, у другого был бы висячий указатель. Глубокое copy выделяет новую память и копирует фактические data, гарантируя, что каждый объект владеет собственными ресурсами.

challenge icon

Задание

Легко

Давайте создадим систему истории сообщений, демонстрирующую важность глубокого копирования, когда ваш class управляет динамической памятью. Вы создадите class MessageLog, который хранит сообщения в динамически выделенном array, и Implement правильный конструктор копирования, чтобы каждая копия имела собственную независимую память.

Вы создадите два файла для организации кода:

  • MessageLog.h: Define class MessageLog, который управляет коллекцией сообщений с использованием динамической памяти. Ваш class должен иметь:
    • Приватные members: указатель на string array для хранения сообщений, capacity — максимальное число сообщений, и count — текущее число сообщений
    • Параметризованный конструктор, который принимает capacity, выделяет array в heap и Initializes count значением 0
    • Конструктор копирования, выполняющий глубокое копирование: выделяющий новую память и копирующий all сообщения из исходного объекта
    • Деструктор, освобождающий выделенную память и выводящий "MessageLog destroyed"
    • Метод addMessage(string msg), который добавляет сообщение, если есть место
    • Метод getCount(), который возвращает текущее количество сообщений
    • Метод getMessage(int index), который возвращает сообщение по указанному индексу
  • main.cpp: Продемонстрируйте, что ваш конструктор копирования создаёт независимую копию. Считайте значение capacity и два сообщения из input, затем:
    • Create MessageLog, called original, с указанным в input capacity
    • Add both сообщения в original
    • Create копию, called backup, используя конструктор копирования
    • Add третье сообщение "New message" только в original
    • Выведите "Original count: <count>"
    • Выведите "Backup count: <count>"
    • Выведите all сообщения из backup, по одному в строке

Формат input будет следующим:

  • Первая строка: capacity (целое число)
  • Вторая строка: первое сообщение (строка)
  • Третья строка: второе сообщение (строка)

Если глубокое копирование реализовано правильно, backup должен содержать 2 сообщения, а original — 3: это доказывает, что они используют независимую память. Без правильного конструктора копирования оба объекта совместно использовали бы один и тот же array, и изменения одного влияли бы на другой!

Include ваш заголовочный файл в main.cpp с помощью #include "MessageLog.h".

Попробуйте сами

#include <iostream>
#include <string>
#include "MessageLog.h"
using namespace std;

int main() {
    // Прочитать ввод
    int capacity;
    cin >> capacity;
    cin.ignore();
    
    string msg1, msg2;
    getline(cin, msg1);
    getline(cin, msg2);

    // TODO: Создать MessageLog с именем 'original' с входной ёмкостью

    // TODO: Добавить оба сообщения в original

    // TODO: Создать копию с именем 'backup' используя конструктор копирования

    // TODO: Добавить "New message" только в original

    // TODO: Print "Original count: <count>"

    // TODO: Вывести "Backup count: <count>"

    // TODO: Вывести все сообщения из backup, по одному на строку

    return 0;
}
quiz iconПроверьте себя

В этом уроке есть небольшой тест. Начните урок, чтобы ответить на вопросы и сохранить прогресс.

Все уроки раздела Объектно-ориентированное программирование

Потренируйтесь самостоятельно: Онлайн-компилятор C++