Конструктор копирования
Часть раздела Объектно-ориентированное программирование путешествия по 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, гарантируя, что каждый объект владеет собственными ресурсами.
Задание
ЛегкоДавайте создадим систему истории сообщений, демонстрирующую важность глубокого копирования, когда ваш class управляет динамической памятью. Вы создадите class MessageLog, который хранит сообщения в динамически выделенном array, и Implement правильный конструктор копирования, чтобы каждая копия имела собственную независимую память.
Вы создадите два файла для организации кода:
MessageLog.h: Define classMessageLog, который управляет коллекцией сообщений с использованием динамической памяти. Ваш class должен иметь:- Приватные members: указатель на string array для хранения сообщений,
capacity— максимальное число сообщений, иcount— текущее число сообщений - Параметризованный конструктор, который принимает capacity, выделяет array в heap и Initializes count значением 0
- Конструктор копирования, выполняющий глубокое копирование: выделяющий новую память и копирующий all сообщения из исходного объекта
- Деструктор, освобождающий выделенную память и выводящий
"MessageLog destroyed" - Метод
addMessage(string msg), который добавляет сообщение, если есть место - Метод
getCount(), который возвращает текущее количество сообщений - Метод
getMessage(int index), который возвращает сообщение по указанному индексу
- Приватные members: указатель на string array для хранения сообщений,
main.cpp: Продемонстрируйте, что ваш конструктор копирования создаёт независимую копию. Считайте значение capacity и два сообщения из input, затем:- Create
MessageLog, calledoriginal, с указанным в input capacity - Add both сообщения в
original - Create копию, called
backup, используя конструктор копирования - Add третье сообщение
"New message"только вoriginal - Выведите
"Original count: <count>" - Выведите
"Backup count: <count>" - Выведите all сообщения из
backup, по одному в строке
- Create
Формат 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;
}
В этом уроке есть небольшой тест. Начните урок, чтобы ответить на вопросы и сохранить прогресс.
Все уроки раздела Объектно-ориентированное программирование
1Основы ООП
Внешние файлыСборка и компиляция C++Заголовочные файлы и файлы исходного кодаПространства имен и область видимостиВведение в ООП на C++Классы и объектыУказатель 'this'Методы (функции-члены)Атрибуты (члены данных)Основы конструкторов и деструкторовИтоги — Простой калькулятор4Свойства классов
Члены экземпляра против статическихГеттеры и сеттерыКонстантные функции-членыКлючевое слово mutableСтатические методы и переменныеДружественные функции и классыИтоги — Менеджер банковских счетов7Наследование
Основы наследованияУровни доступа при наследованииПорядок вызова конструкторов и деструкторовПереопределение методовВиртуальные функции и VTableМножественное наследованиеВиртуальное наследованиеИтоги — Иерархия сотрудников10Обзор STL
Обзор и философия STLКонтейнеры STLИтераторыАлгоритмы STLФункторы и лямбда-выраженияИтоги: частота слов13Паттерны проектирования. Часть 1
Введение в паттерны проектированияПаттерн SingletonFactory и Abstract FactoryПаттерн BuilderПаттерн ObserverПаттерн Strategy2Управление памятью
Стек и кучаУказатели и ссылкиДинамическая память (new/delete)Умные указатели в C++RAII в C++Итоги — Менеджер динамического массива5Инкапсуляция
Спецификаторы доступа в C++Спецификаторы доступа: подробный разборСокрытие данныхStruct против ClassВложенные и внутренние классыИтоги — Система учета студентов8Полиморфизм
Полиморфизм компиляции и времени выполненияПерегрузка функцийВиртуальные функции: повторениеЧистые виртуальные функцииАбстрактные классыПроектирование интерфейсов на C++Dynamic Casting и RTTIИтоги: Калькулятор фигур11Продвинутые концепции ООП
Композиция против наследованияМиксины через CRTPИдиома PimplСтирание типовEnum Classes и строгая типизацияОбработка исключений в ООППользовательские иерархии исключений14Паттерны проектирования. Часть 2
Паттерн КомандаПаттерн АдаптерПаттерн ДекораторПаттерн Шаблонный методПаттерн СостояниеПаттерн КомпоновщикRAII как паттерн3Конструкторы и деструкторы
Конструктор по умолчаниюПараметризованный конструкторКонструктор копированияКонструктор перемещенияСписки инициализации конструктораДелегирующие конструкторыГлубокое погружение в деструкторыПравило трех / пяти / нуляПовторение — класс String6Перегрузка операторов
Введение в перегрузку операторовПерегрузка арифметических операторовПерегрузка операторов сравненияОператоры потоковПерегрузка оператора присваиванияПерегрузка операторов [] и ()Операторы преобразования типовИтоги — класс Matrix9Шаблоны
Шаблоны функцийШаблоны классовСпециализация шаблоновВариативные шаблоныОсновы SFINAE и Type TraitsИтоги — Обобщенный контейнерПотренируйтесь самостоятельно: Онлайн-компилятор C++