2012-02-11 6 views
0

Бьярне Страуструп пишет:Virtual Наследование - база совместно с другими производными классами

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

Я не понимаю этого утверждения. Какие возможные проблемы могут возникнуть?

Чтобы объяснить это, он дает странный пример

class A {        // no constructor 
    // ... 
}; 
class B { 
    public: 
    B();        // default constructor 
    // ... 
}; 
class C { 
    public: 
    C(int);       // no default constructor 
}; 
class D: virtual public A, virtual public B, virtual public C 
{ 
    D() { /*... */ }     // error: no default constructor for C 
    D(int i) : C(i i) { /*... */ }; // ok 
    // ... 
}; 

Имеет ли значение здесь ??

ответ

2

Представьте, что у вас есть виртуальный базовый класс, который нужно инициализировать, вызывая .initialize(42), ровно один раз. Дело в том, что вы не называете это производным классом.

struct X : virtual A { 
    X() { 
    // here 
    } 
}; 

struct Y : virtual A { 
    Y() { 
    // or here 
    } 
}; 

struct Z : X, Y { 
    // what about this  
}; 

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

C++ решает эту проблему для конструкторов, ул. конструкторы для всех виртуальных баз называются ровно один раз в конструкторе самого производного подобъекта; любой конструктор в иерархии должен быть готов вызвать конструктор виртуальной базы (хотя во время выполнения это может не произойти).

+0

: пожалуйста, объясните эту строку в приведенном выше описании «программист вообще не может знать, будет ли база разделяться с другими производными классами». эта строка убивает меня. К чему он ссылается? –

+0

вы можете указать другой пример (кроме конструктора), где функцию базового класса нужно вызывать только один раз –

1

Представьте себе ситуацию, как это:

struct Base 
{ 
    Base() { } 
    virtual void call_me(); 
}; 

struct A : virtual Base { A() { call_me(); } }; 
struct B : virtual Base { B() { call_me(); } }; 

struct Derived : A, B 
{ 
    Derived() 
    : Base()  // virtual base is construced in most-derived 
    , A() 
    , B() 
    { } 
}; 

Теперь предупреждение о том, что промежуточные классы A и B, возможно предположить, что они являются единственными, которые требуют Base::call_me(). Если их наследование Base было не виртуальным, это было бы действительно так, поскольку оба A и B имели бы свой собственный, уникальный базовый класс. Однако, с виртуальным наследованием, это не доA и B, чтобы определить, кто заканчивается как их базовый класс, так как только конечный класс Derived устанавливает фактический единый базовый класс, общий для всех посредников. Таким образом, в нашем примере иA и B в конечном итоге вызывает call_me() на такой же базовый объект.

Мораль в том, что операции, относящиеся к виртуальному базовому классу, должны отвечать за класс , который является самым производным классом. Поскольку это не жесткое понятие, и наследование может быть практически неограничено, это скользкий наклон. Виртуальное, множественное наследование - сложная концепция.

+0

В вашем бывшем: - вы не переопределили callme(). Но вы начинаете называть его классом A и классом B. секунд я думаю, что call_me() будет вызываться дважды сначала A(), а затем B() . Также объясните эту строку: «программист вообще не знает, будет ли база разделяться с другими производными классами». он убивает меня –

+0

@ 10001001058: Автор 'A' может никогда не знать, что * кто-то другой * извлекается из него, а также из других классов, которые происходят из' Base' практически, поэтому 'A' не может предположить, что это единственный, вызывающий 'Base :: call_me'. –

+0

Я думаю, что call_me мы выполним дважды в вашем коде на одном и том же объекте. Но проблема в другом. isn't.It заключается в том, чтобы реализовать функцию базового класса только один раз, которые определены в A и B –

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