2014-12-05 7 views
3

Рассмотрим следующий код:виртуальное наследование и базовый класс базового класса

class A { 
}; 

class B : public A { 
}; 

class C : public B{ 
    public: 
    C() : A() {} // ERROR, A is not a direct base of B 
}; 

В этом случае GCC (4.8.1, C++ 99) дает мне правильную ошибку (я понимаю это поведение):

prog.cpp: 12: 8: ошибка: тип «а» не является прямой базой «с»

Однако, если наследование между в и является виртуальным, этого не происходит:

class A { 
}; 

class B : virtual public A { 
}; 

class C : public B{ 
    public: 
    C() : A() {} // OK with virtual inheritance 
}; 

Почему это работает? Является ли A теперь прямой базой для C компилятором?

+2

Нет, но виртуальная база построена по самому производному классу. –

ответ

0

Почему это работает?

Согласно стандарту (10.1.4 в FIDS), «для каждого отдельного базового слоя, указанного виртуальным, наиболее производный объект должен содержать один подобъект базового класса этого типа».

Виртуальная база разделяется между всеми классами, полученными из нее для экземпляра объекта. Так как конструктор может быть вызван только один раз для данного instaniation объекта, вы должны явно вызвать конструктор в самом производном классе, потому что компилятор не знает, сколько классов используют виртуальную базу. Это связано с тем, что компилятор будет запускаться из конструктора самого базового класса и работать с наиболее производным классом. Классы, которые наследуются от виртуального базового класса напрямую, по стандарту не будут называть их конструктором виртуальных базовых классов, поэтому его нужно вызывать явно.

+0

Почему вы говорите, что классы, наследующие от виртуальной базы, не будут вызывать конструкторы базовых классов? ИМО вам не нужно явно называть это:?. http://coliru.stacked-crooked.com/a/ac1ca38005a848d3 – doc

+0

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

+0

@IdeaHat Я не отрицаю, что вы говорите, но 1) конструктор по умолчанию - это конструктор, поэтому предложение «Классы, которые наследуют ... не будут ... вызывать их конструкторы виртуальных базовых классов ...» не имеет большого смысла ИМО (я знаю, что значит рави, но это не точно) 2) взгляните на вопрос ОП. Он предоставляет код, в котором есть только конструкторы по умолчанию, поэтому я читаю ответ в этом контексте. – doc

2

В целом, поскольку C++ пытается решить проблему наследования алмазов http://en.wikipedia.org/wiki/Multiple_inheritance#The_diamond_problem (будь то хорошее или плохое решение остается в качестве упражнения для читателя).

Все наследование представляет собой комбинацию отношений is-a и has-a ... вы должны создать экземпляр родителя. Если у вас есть следующие классы:

class a; 
class b : a; 
class c : a; 
class d : b,c; 

Затем вы создали экземпляр a для каждого b и c. d не будет знать, какой а использовать.

C++ решает это, разрешив виртуальное наследование, которое является высоконадежным наследованием, которое позволяет b и c совместно использовать один и тот же a, если унаследовано в d (это намного сложнее, чем это, но вы можете прочитать это на своем своя).

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

class a {int x; public: a(int xx) {x=xx;} int get_x() {return x;}}; 
class b : public virtual a { public: b(): a(10){}}; 
class c : public virtual a { public: c(): a(15){}}; 
class d : public virtual b, public virtual c {public: d() : a (20) {}}; 
int main() { 
    d dd; 
    std::cout << dd.get_x() << std::endl;//20, d's constructor "wins" 
    return 0; 
} 

Если d не определить, что a был экземпляр, как это было бы для определения конфликтующих инстанциацию (от b и c). C++ обрабатывает это, заставляя наиболее производный класс в цепочке наследования создавать экземпляры всех родительских классов (выше было бы barf, если d НЕ ЯВЛЯЕТСЯ Явным образом создавал экземпляр a, хотя если a предоставил конструктор по умолчанию, который может быть неявно использован) и игнорирование всех родительских экземпляров.

+0

Но это не было бы barf, если 'a' имел значение по умолчанию ctor. – doc

+0

@doc. Истина будет иметь неявное создание экземпляра с использованием конструктора по умолчанию (и будет игнорировать явное создание 'a' в' b' и 'c') – IdeaHat

0

От N3337, 12.6.2

Initializing bases and members

In the definition of a constructor for a class, initializers for direct and virtual base subobjects and non-static data members can be specified by a ctor-initializer, which has the form

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

Смежные вопросы