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

Обновлено: 29.04.2024

Начиная с 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 так, назначение завершается успешно.

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

Репутация: нет
Всего: 2

Код

class Base
public:
Base()<>
Base(char* arg1, int arg2)<>
Base(const Base &object)<>
virtual ~Base()<>

Base& operator=(const Base &object)<>

virtual void Method1() = 0;
void Method2()<>;

private:
int size;
char* data;
>;

class Derived: public Base
public:
Derived(): Base()<>
Derived(char * arg1, int arg2, char *arg3, int arg4): Base(arg3, arg4)<>
Derived(const Derived& object)< . >
~Derived()<>

Derived& operator=(const Derived &object)<>

virtual void Method1()<>

Собственно, вопрос следующий. Как правильно реализовать конструктор копирования класса-потомка, чтобы внутренний объект класса-родителя также был корректно скопирован?
Есть два варианта, которые я сам могу предложить:
1.

Код

Derived(const Derived& object): Base((Base &) object)
/* Копирование данных в объект класса-потомка*/
>
Код

Derived(const Derived& object)
*((Base *)this) = *((Base *) &object);
/* Копирование данных в объект класса-потомка*/
>

Есть ли из них верный? Если оба, какой работает "правильней" ("быстрей")? Если нет, то как правильно вести себя в такой ситуации?

Репутация: 63
Всего: 196

Но, существует вероятность "срезки" - если ты сделаешь класс D2 потомок D, а потом скопируешь в объект D, то скопируются только общие данные.
Обычно, полиморфные классы не допускают подобного копирования. Если нужно копирование, то делать необходимо через соответствующий виртуальный метод и new:

Код
class B
public:
virtual ~B()<> //виртуальный деструктор должен быть во всех классах, предполагающих наследование
virtual B* copy() const = 0;
protected:
B(const B &other) <. >
>;

Репутация: нет
Всего: 2

А если у меня возникнет необходимость передать объект класса D в функцию:

Если конструктор копирования скрыт, такая ситуация обработается неверно. Что делать здесь?

Репутация: 63
Всего: 196

Цитата(Brain89 @ 15.11.2011, 19:27 )
Что делать здесь?


использовать константные ссылки. И вообще, без крайней необходимости по значению следует передавать только стандартные типы.

Репутация: нет
Всего: 2

Репутация: 1
Всего: 1

Цитата(bsa @ 15.11.2011, 17:54)
Brain89, правильнее это делать так:

Но, существует вероятность "срезки" - если ты сделаешь класс D2 потомок D, а потом скопируешь в объект D, то скопируются только общие данные.
Обычно, полиморфные классы не допускают подобного копирования. Если нужно копирование, то делать необходимо через соответствующий виртуальный метод и new:

Код
class B
public:
virtual ~B()<> //виртуальный деструктор должен быть во всех классах, предполагающих наследование
virtual B* copy() const = 0;
protected:
B(const B &other) <. >
>;

а можно пример, а то я не совсем понял при какой варианте срезка произойдет.

Код

class B<>;
class D: public B<>;
class D2: public D<>;

D d;
D2 d2;
d = d2; // Тут как не крути всегда будет срезка.

D* d;
D2* d2;
d = d2; // тут и копирования не будет

или же ваш вариант и нацелен на то что бы явно запретить вызов конструктора копирование по умолчанию.

Репутация: нет
Всего: 2

Код

class Base
public:
Base()<>
Base(char* arg1, int arg2)<>
Base(const Base &object)<>
virtual ~Base()<>

Base& operator=(const Base &object)<>

virtual void Method1() = 0;
void Method2()<>;

private:
int size;
char* data;
>;

class Derived: public Base
public:
Derived(): Base()<>
Derived(char * arg1, int arg2, char *arg3, int arg4): Base(arg3, arg4)<>
Derived(const Derived& object)< . >
~Derived()<>

Derived& operator=(const Derived &object)<>

virtual void Method1()<>

Собственно, вопрос следующий. Как правильно реализовать конструктор копирования класса-потомка, чтобы внутренний объект класса-родителя также был корректно скопирован?
Есть два варианта, которые я сам могу предложить:
1.

Код

Derived(const Derived& object): Base((Base &) object)
/* Копирование данных в объект класса-потомка*/
>
Код

Derived(const Derived& object)
*((Base *)this) = *((Base *) &object);
/* Копирование данных в объект класса-потомка*/
>

Есть ли из них верный? Если оба, какой работает "правильней" ("быстрей")? Если нет, то как правильно вести себя в такой ситуации?

Репутация: 63
Всего: 196

Но, существует вероятность "срезки" - если ты сделаешь класс D2 потомок D, а потом скопируешь в объект D, то скопируются только общие данные.
Обычно, полиморфные классы не допускают подобного копирования. Если нужно копирование, то делать необходимо через соответствующий виртуальный метод и new:

Код
class B
public:
virtual ~B()<> //виртуальный деструктор должен быть во всех классах, предполагающих наследование
virtual B* copy() const = 0;
protected:
B(const B &other) <. >
>;

Репутация: нет
Всего: 2

А если у меня возникнет необходимость передать объект класса D в функцию:

Если конструктор копирования скрыт, такая ситуация обработается неверно. Что делать здесь?

Репутация: 63
Всего: 196

Цитата(Brain89 @ 15.11.2011, 19:27 )
Что делать здесь?


использовать константные ссылки. И вообще, без крайней необходимости по значению следует передавать только стандартные типы.

Репутация: нет
Всего: 2

Репутация: 1
Всего: 1

Цитата(bsa @ 15.11.2011, 17:54)
Brain89, правильнее это делать так:

Но, существует вероятность "срезки" - если ты сделаешь класс D2 потомок D, а потом скопируешь в объект D, то скопируются только общие данные.
Обычно, полиморфные классы не допускают подобного копирования. Если нужно копирование, то делать необходимо через соответствующий виртуальный метод и new:

Код
class B
public:
virtual ~B()<> //виртуальный деструктор должен быть во всех классах, предполагающих наследование
virtual B* copy() const = 0;
protected:
B(const B &other) <. >
>;

а можно пример, а то я не совсем понял при какой варианте срезка произойдет.

Код

class B<>;
class D: public B<>;
class D2: public D<>;

D d;
D2 d2;
d = d2; // Тут как не крути всегда будет срезка.

D* d;
D2* d2;
d = d2; // тут и копирования не будет

или же ваш вариант и нацелен на то что бы явно запретить вызов конструктора копирование по умолчанию.

Репутация: нет
Всего: 2

Код

class Base
public:
Base()<>
Base(char* arg1, int arg2)<>
Base(const Base &object)<>
virtual ~Base()<>

Base& operator=(const Base &object)<>

virtual void Method1() = 0;
void Method2()<>;

private:
int size;
char* data;
>;

class Derived: public Base
public:
Derived(): Base()<>
Derived(char * arg1, int arg2, char *arg3, int arg4): Base(arg3, arg4)<>
Derived(const Derived& object)< . >
~Derived()<>

Derived& operator=(const Derived &object)<>

virtual void Method1()<>

Собственно, вопрос следующий. Как правильно реализовать конструктор копирования класса-потомка, чтобы внутренний объект класса-родителя также был корректно скопирован?
Есть два варианта, которые я сам могу предложить:
1.

Код

Derived(const Derived& object): Base((Base &) object)
/* Копирование данных в объект класса-потомка*/
>
Код

Derived(const Derived& object)
*((Base *)this) = *((Base *) &object);
/* Копирование данных в объект класса-потомка*/
>

Есть ли из них верный? Если оба, какой работает "правильней" ("быстрей")? Если нет, то как правильно вести себя в такой ситуации?

Репутация: 63
Всего: 196

Но, существует вероятность "срезки" - если ты сделаешь класс D2 потомок D, а потом скопируешь в объект D, то скопируются только общие данные.
Обычно, полиморфные классы не допускают подобного копирования. Если нужно копирование, то делать необходимо через соответствующий виртуальный метод и new:

Код
class B
public:
virtual ~B()<> //виртуальный деструктор должен быть во всех классах, предполагающих наследование
virtual B* copy() const = 0;
protected:
B(const B &other) <. >
>;

Репутация: нет
Всего: 2

А если у меня возникнет необходимость передать объект класса D в функцию:

Если конструктор копирования скрыт, такая ситуация обработается неверно. Что делать здесь?

Репутация: 63
Всего: 196

Цитата(Brain89 @ 15.11.2011, 19:27 )
Что делать здесь?


использовать константные ссылки. И вообще, без крайней необходимости по значению следует передавать только стандартные типы.

Репутация: нет
Всего: 2

Репутация: 1
Всего: 1

Цитата(bsa @ 15.11.2011, 17:54)
Brain89, правильнее это делать так:

Но, существует вероятность "срезки" - если ты сделаешь класс D2 потомок D, а потом скопируешь в объект D, то скопируются только общие данные.
Обычно, полиморфные классы не допускают подобного копирования. Если нужно копирование, то делать необходимо через соответствующий виртуальный метод и new:

Код
class B
public:
virtual ~B()<> //виртуальный деструктор должен быть во всех классах, предполагающих наследование
virtual B* copy() const = 0;
protected:
B(const B &other) <. >
>;

а можно пример, а то я не совсем понял при какой варианте срезка произойдет.

Код

class B<>;
class D: public B<>;
class D2: public D<>;

D d;
D2 d2;
d = d2; // Тут как не крути всегда будет срезка.

D* d;
D2* d2;
d = d2; // тут и копирования не будет

или же ваш вариант и нацелен на то что бы явно запретить вызов конструктора копирование по умолчанию.

Итак, для линии D d(d1) конструктор копирования D класс называется. Во время наследования нам нужно явно вызвать конструктор копирования базового класса, в противном случае вызывается только конструктор по умолчанию базового класса. Я понял до здесь.

Моя проблема:

Теперь я хочу вызвать конструктор копирования всех базовых классов во время D d(d1) выполнение. Для этого, если я попробую ниже
D(const D & obj) : A(obj), B(obj), C(obj)
Тогда я получаю эту ошибку:
ошибка: 'class A A::A' is inaccessible within this context

Как решить проблему. Я хочу скопировать конструктор A , B а также C когда копировать конструктор D вызывается. Это может быть очень маленькое изменение, но я не получаю.

Решение

Во-первых, давайте изменим ваше наследство, так как в настоящее время оно является частным:

Теперь, в вашем конструкторе копирования, явно укажите, что конструкторы копирования A а также B а также C должен называться:

И вывод будет по желанию ( 2468 ).

Зачем?

Когда у нас есть виртуальные базовые классы, они должны быть инициализированы самым производным классом, в противном случае была бы двусмысленность относительно B или же C например, отвечает за строительство A ,

  • Во-первых, и только для конструктора самого производного класса (1.8),
    виртуальные базовые классы инициализируются в порядке их появления на
    первый слева направо обход глубины направленного ациклического графа
    базовые классы, где «слева направо» — порядок появления
    базовые классы в производном списке базовых спецификаторов.

В частности, если вы определяете конструктор копирования и опускаете список конструкторов копирования, которые он должен вызывать, то конструкторы по умолчанию будет использоваться.

Другие решения

То, как вы унаследовали ваши классы, все они используют private наследование.

Изменяя наследство B от A а также C от A быть protected или же public Вы можете решить проблему.

а затем обновить D Копировать конструктор для:

PS Меня сбивает с толку, что конструктор по умолчанию работает даже с private наследование.

Альтернативное решение, которое не требует изменения модификаторов наследования класса B или же C :

Относительно проверки доступа для конструкторов: от [Class.access] / 6

Все элементы управления доступом в пункте [class.access] влияют на возможность
получить доступ к имени члена класса из объявления конкретной сущности
… [Примечание: этот доступ также относится к неявные ссылки на
конструкторы, функции преобразования и деструкторы. — конец примечания]

Специальные функции-члены подчиняются обычным правилам доступа. [ Пример:
Объявление защищенного конструктора гарантирует, что только производные классы
и друзья могут создавать объекты, используя его. — конец примера]

Относительно инициализации подобъектов базового класса: от [Class.base.init] / 9

В не делегирующем конструкторе, если данное потенциально построено
подобъект не обозначается идентификатором mem-initializer-id (включая
случай, когда нет mem-initializer-list, потому что конструктор
не имеет инициализатора ctor), тогда … в противном случае
по умолчанию инициализируется

Отсутствие каких-либо т е р-инициализатор для подобъекта базового класса означает, что подобъект инициализирован по умолчанию; от [Dcl.init] / 7

Инициализация по умолчанию объекта типа T означает: …
Вызванный таким образом конструктор вызывается с пустым списком аргументов,
инициализировать объект.

Так что отсутствие какой-либо базы в т е р-инициализатор запрос на инициализацию по умолчанию для этой базы, что означает вызов конструктора по умолчанию.

Отсутствие упоминания о базовом классе не имеет значения; в любом случае, конструктор не имеет имени и не назван в т е р-инициализатор, на него ссылаются либо явно, либо неявно. В стандарте нет ничего, что предполагало бы, что контроль доступа не должен выполняться в таком случае.

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

В любом случае вы можете изменить наследование с частного на защищенное и даже добавить путь к виртуальному базовому классу:

Таким образом, виртуальный базовый класс A все еще закрыт, но доступен для D ,

Читайте также: