2014-11-13 5 views
3

У меня есть 2 базовых класса (B1 и B2), которые получены из общего базового класса (B), где они имеют общую переменную (пусть: int x; из базы B), на 1-й базе x=0, на 2-й базе x=10 (значения по умолчанию приведены в конструкторах B1, B2).Множественное наследование

Визуально:

class B 
{ 
    int x; 

protected: 

    B(int x) : x{x}{} 
}; 

class B1 : public B 
{ 
protected: 

    B1() : B(0){} 
}; 

class B2 : public B 
{ 
protected: 

    B2() : B(10){} 
}; 

Теперь, если я получить еще один класс:

class D : virtual public B1, virtual public B2 
{ 
public: 

    D() : B1{}, B2{}{} 
}; 

Вот только одна копия х будет доступен как в виртуальной концепции, теперь, если я пытаюсь получить доступ к значению х с объектом производного класса, экземпляр x которого я получу в O/p (x=0 или x=10), и почему?

+2

Показать фактический код. – Brian

+0

Я верю, что вы не можете создавать экземпляры класса B1 и B2, потому что они абстрактны из-за виртуального наследования .. Для получения дополнительной информации посетите: http://www.cprogramming.com/tutorial/virtual_inheritance.html –

+3

@ReyRajesh Что нужно делать виртуальному наследованию с абстрактностью (или нет) класса? –

ответ

5

Чтобы использовать виртуальное наследование, база B должна быть объявлена ​​как виртуальная как в B1, так и в B2. Без этого, у вас есть не виртуальное наследование Б.

Если у вас есть не-виртуального наследования, то у вас есть два B базы в D, так что вы не можете получить доступ к x в D без квалификации как B1::x или B2::x

Если у вас есть виртуальное наследование, то у вас есть только один B и один x, поэтому два задания к нему (x=0 и x=10) будет происходить в любом порядке вы сделали их, и в зависимости от того один было позже перезаписать установленное значение по сравнению с предыдущей (как и с простой переменной x wi два назначения).

+0

Отлично! Init выполняется в ctor B1 и B2. Есть ли какие-либо гарантии в отношении порядка здесь (f.ex осталось до rgt из-за комы) в соответствии с std? – Christophe

+0

Я не думаю, что последний абзац о виртуальном наследовании правильный. Самый производный класс должен построить виртуальный базовый класс, т. Е. С вызовом конструктора виртуального базового класса в списке инициализатора члена (если он не является конструктором по умолчанию, который будет называться неявно). См. Мой ответ. – Oguk

+0

@ Крис Додд, вы правы, я хочу знать, что происходит, когда я наследую класс практически? (что я имею в виду, если я ставлю одну виртуальную функцию в класс, она станет полиморфной и сохранит V.table и т. д.) так же, как это происходит, когда я получаю класс практически?) – NDestiny

2

В вашей установки, как вы есть, B не фактически унаследовали практически, потому что вы должны объявить виртуальное наследование B в обоих B1 и B2 (это всегда должно происходить на самом низком уровне, если две «ветви» как ожидается, будет объединено выше в иерархии наследования классов), т.е.

class B1 : virtual public B 
{ 
protected: 

    B1() : B(0){} 
}; 

class B2 : virtual public B 
{ 
protected: 

    B2() : B(10){} 
}; 

Если вы сделаете это, инициализация B была бы совершенно иной, потому что есть специальные правила для построения виртуальных базовых классов:

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

D() : B1(), B2(){} 

и поэтому не называйте свой B конструктор явно, компилятор будет считать, что вы хотите вызвать B конструктору по умолчанию. Но ваш класс B не имеет конструктор по умолчанию, так что вы получите ошибку компиляции, как это:

prog.cpp: In constructor ‘D::D()’: 
prog.cpp:31:20: error: no matching function for call to ‘B::B()’ 
    D() : B1(), B2(){} 
        ^

Таким образом, вы должны сделать что-то вроде

class D : public B1, public B2 
{ 
public: 

    D() : B(99), B1(), B2(){} 
}; 

и это также решает свой вопрос : Значение x будет таким, каким хочет этот производный класс (в данном случае 99). Таким образом, нет никакой двусмысленности.

PS: Вы также видите, что ваш вопрос лежит в основе того, почему имеет смысл иметь специальное правило о построении виртуального базового класса по самому производному классу.

+0

, даже если я изменяю порядок как 1) D(): B (99), B1(), B2() {} 2) D(): B1(), B2(), B (99) {} -> значение x будет равно 99. что здесь нет эффекта B1, B2, мы закончили запись значения x в конструкторе B1 и B2 справа? – NDestiny

+0

Для виртуального наследования конструктор базового класса * только * вызывается из самого производного класса. Таким образом, вызовы конструктора 'B' из конструкторов' B1' и 'B2' пропускаются. Обратите внимание, что это было бы иначе, если бы вы выполняли простые назначения ('x = some_number;') внутри конструкторов 'B1' и' B2'. Они не будут пропущены. В этом случае порядок станет важным: конструктор виртуального базового класса всегда выполняется первым, независимо от того, какой порядок вы указали в списке (и компилятор должен предупредить вас о вашем случае «2»), где «B (99) 'приходит последним). – Oguk

+0

Просто примечание: я просто изменил все вызовы конструктора типа 'D(): B {99}, B1 {}, B2 {} {}' в ответе на «традиционный» синтаксис, то есть 'D(): B (99), B1(), B2() {} ', потому что некоторые версии gcc, похоже, имеют проблемы с первым. Я просто разместил [вопрос] (http://stackoverflow.com/q/26914076/3749537), чтобы убедиться, что это действительно ошибка gcc. – Oguk

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