Рассмотрим следующий код:
#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
вызывается. Это может быть очень маленькое изменение, но я не получаю.
Во-первых, давайте изменим ваше наследство, так как в настоящее время оно является частным:
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),
виртуальные базовые классы инициализируются в порядке их появления на
первый слева направо обход глубины направленного ациклического графа
базовые классы, где «слева направо» — порядок появления
базовые классы в производном списке базовых спецификаторов.
В частности, если вы определяете конструктор копирования и опускаете список конструкторов копирования, которые он должен вызывать, то конструкторы по умолчанию будет использоваться.
То, как вы унаследовали ваши классы, все они используют 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
наследование.
Альтернативное решение, которое не требует изменения модификаторов наследования класса 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";}
};
Относительно проверки доступа для конструкторов: от [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
,