Когда вызывается конструктор копирования c
Перейти к содержимому

Когда вызывается конструктор копирования c

Когда вызывается конструктор копирования c

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

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

Конструктор копирования, в отличии от других, в качестве параметра принимает константную ссылку на объект класса.

Данный конструктор вызывается всякий раз, когда создаётся новый объект и для его инициализации берётся значение существующего объекта того же типа. Например, в следующих случаях:

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

В чём же проблема отсутствия конструктора копирования при выделении в классе динамической памяти? Дело в том, что при отсутствии явного описания, он описывается неявно. Неявный конструктор выполняет поверхностное копирование, т. е. просто дублирует биты из переменных. Таким образом, вместо данных из динамической памяти, копируется адреса на них. В результате, появляется несколько объектов, указывающих на одну область памяти. При изменении этой области через один объект, она также изменится и в другом, что в большинстве случаев является нежелательным поведением. Поэтому в классах, работающих с динамической памятью, необходимо всегда явно объявлять конструктор копирования (см. пример в конце). Как вариант исключения данной проблемы, можно поместить конструктор копирования в приватной области класса, что вовсе запретит выполнять копирование.

2. Перегруженная операция присваивания

Перегруженная операция присваивания используется при присваивании одного объекта другому существующему объекту. Здесь присутствует такая же проблема, что и в конструкторе копирования. К тому же, у объекта, которому присваивается значение, уже может быть выделена динамическая память. Перед присваиванием новых данных, выделенную ранее память необходимо очистить, чтобы не допустить её утечки (см. пример в конце). Также необходимо обработать случай самоприсваивания. В противном случае, данные в динамической памяти просто будут утеряны. Аналогично копированию, присваивание также можно запретить, поместив операцию в приватной области класса.

3. Деструктор

Деструктор вызывается перед удалением объекта и предназначен для освобождения всех используемых ресурсов. Чтобы не допустить утечки памяти, в деструкторе необходимо её очистить.

4. Пример

Стоить отметить, что во всех трёх функциях память должна выделяться и удаляться одинаковым образом. Т. е. нельзя в одном случае использовать delete, а в другом delete[].

Копирование конструкторов и операторов присваивания копирования (C++)

Начиная с C++11, в языке поддерживаются два типа присваивания: назначение копирования и перемещение. В этой статье «присваивание» означает «присваивание копированием», если явно не указано другое. Сведения о назначении перемещения см. в разделе «Конструкторы перемещения» и «Операторы присваивания перемещения» (C++).

Как при операции назначения, так и при операции инициализации выполняется копирование объектов.

Назначение: когда одному объекту присваивается значение другого объекта, первый объект копируется во второй объект. Таким образом, этот код копирует значение b в a :

Инициализация: инициализация происходит при объявлении нового объекта, при передаче аргументов функции по значению или при возвращении значения из функции.

Можно определить семантику копии объектов типа класса. Рассмотрим для примера такой код:

Приведенный выше код может означать копирование содержимого ФАЙЛА 1. DAT в FILE2. DAT или это может означать «игнорировать FILE2″. DAT и сделайте b второй дескриптор в FILE1.DAT». Необходимо присоединить соответствующую семантику копирования к каждому классу следующим образом:

Используйте оператор operator= присваивания, который возвращает ссылку на тип класса и принимает один параметр, передаваемый по const ссылке, например ClassName& operator=(const ClassName& x); .

Используйте конструктор копирования.

Если вы не объявляете конструктор копирования, компилятор создает конструктор копирования с типом члена. Аналогичным образом, если оператор присваивания копирования не объявлен, компилятор создает для вас оператор назначения копирования с помощью члена. Объявление конструктора копирования не подавляет оператор присваивания копирования, созданного компилятором, и наоборот. Если вы реализуете один из них, рекомендуется также реализовать другой. При реализации обоих значений кода ясно.

Конструктор копирования принимает аргумент типа ClassName& , где ClassName — имя класса. Пример:

По возможности сделайте тип аргумента const ClassName& конструктора копирования. Это предотвращает случайное изменение скопированного объекта конструктором копирования. Он также позволяет копировать из const объектов.

Конструкторы копии, создаваемые компилятором

Конструкторы копирования, созданные компилятором, такие как пользовательские конструкторы копирования, имеют один аргумент типа «ссылка на имя класса«. Исключением является то, что все базовые классы и классы-члены имеют конструкторы копирования, объявленные как принимающие один аргумент const типа class-name&. В таком случае аргумент конструктора копирования, созданного компилятором, также const является .

Если тип аргумента конструктору копирования не const является, инициализация путем копирования const объекта приводит к ошибке. Обратный аргумент не имеет значения true: если аргумент имеет значение const , можно инициализировать, скопировав объект, который не const является.

Операторы присваивания, созданные компилятором, соответствуют одному и тому же шаблону. const Они принимают один аргумент типа ClassName& , если только операторы присваивания во всех базовых классах и классах-членах не принимают аргументы типа const ClassName& . В этом случае созданный оператор присваивания для класса принимает const аргумент.

Если виртуальные базовые классы инициализированы конструкторами копирования( созданными компилятором или определяемыми пользователем), они инициализируются только один раз: в момент создания.

Последствия аналогичны конструктору копирования. Если тип аргумента не const является, назначение из const объекта приводит к ошибке. Обратный аргумент не имеет значения: если const значение присвоено значению, которое не const так, назначение завершается успешно.

Дополнительные сведения о перегруженных операторах присваивания см. в разделе «Назначение».

В каких ситуациях вызывается конструктор копирования C ++?

Я знаю следующие ситуации в C ++, когда вызывается конструктор копирования:

когда существующему объекту присваивается объект его собственного класса

если функция получает в качестве аргумента, переданного по значению, объект класса

когда функция возвращает (по значению) объект класса

Пожалуйста, не стесняйтесь исправлять любые мои ошибки; но мне более любопытно, есть ли другие ситуации, в которых вызывается конструктор копирования.

Возможно, я ошибаюсь, но этот класс позволяет вам видеть, что и когда вызывается:

производит это как результат:

Еще одна интересная вещь, допустим, у вас есть следующий код:

Это происходит потому, что когда вы назначаете указатель, он ничего не делает с фактическим объектом.

When an existing object is assigned an object of it own class

Не обязательно. Этот вид присваивания называется копированием , то есть оператор присваивания класса будет вызываться для выполнения поэлементного присваивания всех членов данных. Фактическая функция MyClass& operator=(MyClass const&)

Копирующий конструктор здесь не вызывается . Это связано с тем, что оператор присваивания принимает ссылку на свой объект, и поэтому копирование не выполняется.

Назначение копирования отличается от инициализации копирования, потому что инициализация копирования выполняется только при инициализации объекта. Например:

Первое выражение инициализируется y копированием x . Он вызывает конструктор копирования MyClass(MyClass const&) .

Как уже упоминалось, x = y это вызов оператора присваивания.

(Существует также нечто, называемое copy-elison, при котором компилятор игнорирует вызовы конструктора копирования. Ваш компилятор, скорее всего, использует это).

If a functions receives as argument, passed by value, an object of a class

Это правильно. Однако обратите внимание, что в C ++ 11 if a является значением x и MyClass имеет соответствующий конструктор MyClass(MyClass&&) , a его можно переместить в параметр.

(Конструктор копирования и конструктор перемещения являются двумя функциями-членами класса, создаваемыми компилятором по умолчанию. Если вы не предоставите их самостоятельно, компилятор щедро сделает это за вас при определенных обстоятельствах).

When a function returns (by value) an object of the class

Благодаря оптимизации возвращаемого значения , как упоминалось в некоторых ответах, компилятор может удалить вызов конструктора копирования. Используя параметр компилятора -fno-elide-constructors , вы можете отключить copy-elison и увидеть, что конструктор копирования действительно будет вызываться в этих ситуациях.

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *