Как вызвать конструктор копирования всех базовых классов для копирования самого производного объекта класса в алмазном наследовании в C ++?

Рассмотрим следующий код:

#include<iostream>
using namespace std;
class A
{
public:
A() {cout << "1";}
A(const A &obj) {cout << "2";}
};

class B: virtual A
{
public:
B() {cout << "3";}
B(const B & obj) {cout<< "4";}
};

class C: virtual A
{
public:
C() {cout << "5";}
C(const C & obj) {cout << "6";}
};

class D:B,C
{
public:
D()  {cout << "7";}
D(const D & obj) {cout << "8";}
};

int main()
{
D d1;
cout << "\n";
D d(d1);
}

Вывод программы ниже:

1357
1358

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

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

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

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

6

Решение

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

class B : virtual protected A {...};
class C : virtual protected A {...};

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

class D : protected B, protected C {
D(const D & obj) : A(obj), B(obj), C(obj) {cout << "8";}
};

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

Зачем?

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

§12.6.2, (13.1):

В не делегирующем конструкторе инициализация происходит в следующем порядке:

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

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

9

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

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

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

class B : protected virtual A
{
...
}

class C : protected virtual A
{
...
}

или же

class B : public virtual A
{
...
}

class C : public virtual A
{
...
}

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

D(const D & obj) : A(obj), B(obj), C(obj) {cout <<"8";}

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

3

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

class A
{
public:
A() {cout << "1";}
A(const A &obj) {cout << "2";}
};

class B: virtual A
{
public:
B() {cout << "3";}
B(const B & obj) {cout<< "4";}
};

class C: virtual A
{
public:
C() {cout << "5";}
C(const C & obj) {cout << "6";}
};

class D:B,C,virtual A
{
public:
D() {cout << "7";}
D(const D & obj) : A(obj), B(obj), C(obj) {cout << "8";}
};
0

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

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

так же, [Class.access] / 4

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

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

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

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

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

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

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

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

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

class D: B, C, virtual A
{

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

0