3

I have 2 base classes (B1 and B2) which are derived from common Base class(B), where they have a common variable (let: int x; from base B), in 1st base x=0, in the 2nd base x=10 (default values given in B1,B2 constructors).

Visually:

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){}
};

Now if I derive one more class:

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

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

Here only one copy of x will be available as per virtual concept, now if I try to access x value with derived class object which instance of x I will get in O/p (x=0 or x=10), and why?

defube
  • 2,395
  • 1
  • 22
  • 34
NDestiny
  • 1,133
  • 1
  • 12
  • 28
  • 2
    Please show actual code. – Brian Bi Nov 13 '14 at 05:54
  • I beleive you cannot instantiate class B1 and B2 because they are abstract due to virtual inheritance.. For more info visit: http://www.cprogramming.com/tutorial/virtual_inheritance.html – Rey Rajesh Nov 13 '14 at 06:05
  • 3
    @ReyRajesh What does virtual inheritance has to do with the abstractness (or not) of a class? – T.C. Nov 13 '14 at 06:11
  • Because class B1 and B2 are virtual.. If B1 wants to access elements of B2 It cannot instantiate the class B2, When a class cannot be instantiated, it is abstract isn't it..? I think I got it wrong.. – Rey Rajesh Nov 13 '14 at 06:16
  • 4
    @ReyRajesh What are you going on about? Virtual inheritance doesn't have anything to do with whether a class is abstract or not. – Praetorian Nov 13 '14 at 06:21
  • 1
    Okay.. I understood wrongly from cprogramming.com/tutorial/virtual_inheritance.html – Rey Rajesh Nov 13 '14 at 06:26

2 Answers2

5

In order to use virtual inheritance, base B must be declared as virtual in both B1 and B2. Without that, you have non-virtual inheritance of B.

If you have non-virtual inheritance, then you have two B bases in D, so you can't access x in D without qualifying it as B1::x or B2::x

If you have virtual inheritance, then you only have one B and one x, so the two assignments to it (x=0 and x=10) will happen in whichever order you did them in, and whichever one was later will overwrite the value set by the earlier one (much as with a simple variable x with two assignments).

Chris Dodd
  • 119,907
  • 13
  • 134
  • 226
  • Excellent ! The init is done in the ctor of B1 and B2. Is there any guarantee about the order here (f.ex left to rgt because of coma) according to std ? – Christophe Nov 13 '14 at 07:35
  • I don't think the last paragraph about virtual inheritance is correct. The most derived class has to construct the virtual base class, i.e. with a call to the virtual base class's constructor in the member initializer list (if it's not the default constructor, which would be called implicitly). See my answer. – Oguk Nov 13 '14 at 08:10
  • @Chris Dodd, you are right, I want to know what happens when I inherit a class virtually ? (what I mean is if I put one virtual function I a class it will become polymorphic and maintain V.table etc... in the same way what happens when I derive class virtually ? ) – NDestiny Nov 13 '14 at 10:54
  • @Oguk: While B will be initialized from D, you don't need to do it explicitly -- it will implicitly use the default ctor. The way the OP described his code, he has assignments to `x` (somewhere), not initializations of it. Assignments can happen anywhere and need not be in constructors. – Chris Dodd Nov 13 '14 at 14:58
  • @Durga: With your edited example, if you change `B` to be inherited virtually, it won't compile, as there's no default ctor for `B`, and that's what `D` tries to (implicitly) use. – Chris Dodd Nov 13 '14 at 15:04
  • @ChrisDodd: In his code, `x` is initialized in the constructor of `B1` and `B2`, which is the info I had when I wrote my answer. The first comment above by Christophe also implies the misconception that `B` is still initialized from `B1` and `B2`. As for the implicit initialization: My answer explicitly states that the default constructor will be called implicitly if no initialization is done in the most derived class's member initialization list. – Oguk Nov 13 '14 at 16:02
  • @Oguk, yes the OP edited his question to change it from assignment to initialization in the B1/B2 ctors. – Chris Dodd Nov 13 '14 at 18:48
2

In your setup as you have it, B is not actually inherited virtually, because you would have to declare virtual inheritance for B in both B1 and B2 (it always has to happen at the lowest level if the two "branches" are expected to be merged higher up in the class inheritance hierarchy), i.e.

class B1 : virtual public B
{
protected:

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

class B2 : virtual public B
{
protected:

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

If you do that, initialization of B would be completely different, because there are special rules for the construction of virtual base classes:

In virtual inheritance, the virtual base class is always initialized by the most derived class. Thus, as you have implemented the constructor of D as

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

and therefore don't call your B constructor explicitly, the compiler will assume that you want to call Bs default constructor. But your class B does not have a default constructor, so you would get a compiler error like this:

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

Therefore, you would have to do something like

class D : public B1, public B2
{
public:

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

and this also solves your question: The value of x will be whatever the most derived class wants it to be (99 in this case). Thus, there is no ambiguity.

PS: You also see, your question is at the heart of why it makes sense to have the special rule about virtual base class construction by the most derived class.

Oguk
  • 1,127
  • 7
  • 13
  • even if I change the order as 1) D() : B(99), B1(), B2(){} 2)D() : B1(), B2(),B(99) {} -> the value of x will be 99 only. whay there is no effect of B1, B2 here, we are over writing the value of x in B1 and B2 constructor right ? – NDestiny Nov 13 '14 at 11:02
  • For virtual inheritance, the constructor of the base class is *only* called from the most derived class. So, the calls to `B`'s constructor from `B1`'s and `B2`'s constructors are skipped. Note that this would be different if you did plain assignments (`x = some_number;`) within the constructors of `B1` and `B2`. These would not be skipped. In that case, the order would become important: The constructor of the virtual base class is always executed first, no matter what order you specify them in the list (and the compiler should warn you about your case "2)", where `B(99)` comes last). – Oguk Nov 13 '14 at 16:24
  • Just a note: I just changed all constructor calls of the type `D() : B{99}, B1{}, B2{}{}` in my answer to the "traditional" syntax, i.e. `D() : B(99), B1(), B2(){}` because some versions of gcc seem to have problems with the former. I just posted a [question](http://stackoverflow.com/q/26914076/3749537) to confirm this is indeed a bug of gcc. – Oguk Nov 13 '14 at 17:00
  • +1 for the explanations about virtual base being intialized by the most derived class, which makes this an excellent and very complete answer ! – Christophe Nov 13 '14 at 19:43