Link to home
Start Free TrialLog in
Avatar of Pra Sys
Pra SysFlag for India

asked on

The order of construction of classes in virtual multiple inheritance in C++

Hi,

This is just a basic C++ question related to virtual base classes. I have below simple code with no methods, no data members, just empty classes with trivial constructors and destructors for debugging purpose -

#include <iostream>

using namespace std;

class L {
public:
    L(){
        cout << "L constructed" << endl;
    }
    ~L(){
        cout << "L destructed" << endl;
    }
};

class A : public virtual L {
public:
    A(){
        cout << "A constructed" << endl;
    }
    ~A(){
        cout << "A destructed" << endl;
    }
};

class B : public virtual L {
public:
    B(){
        cout << "B constructed" << endl;
    }
    ~B(){
        cout << "B destructed" << endl;
    }
};

class C  : public A, public B {
public:
    C(){
        cout << "C constructed" << endl;
    }
    ~C(){
        cout << "C destructed" << endl;
    }
};

class D : public L {
public:
    D(){
        cout << "D constructed" << endl;
    }
    ~D(){
        cout << "D destructed" << endl;
    }
};

class E : public D, public C{
public:
    E(){
        cout << "E constructed" << endl;
    }
    ~E(){
        cout << "E destructed" << endl;
    }
};

int main()
{
    E e;
    return 0;
}

Open in new window


Output is here -

L constructed
L constructed
D constructed
A constructed
B constructed
C constructed
E constructed
--------This is my separation line between construction and destruction-----------
E destructed
C destructed
B destructed
A destructed
D destructed
L destructed
L destructed


My question is this. The 1st line in the output is for construction of L part of C class, I think so. Why it was created way before D?? Why not after construction of D and L part of D?  The way E inherits from D and C, D should have been constructed first and then C. Or am I interpreting it incorrectly??

Thanks.
ASKER CERTIFIED SOLUTION
Avatar of trinitrotoluene
trinitrotoluene
Flag of Australia image

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
remember in your example D is inheriting from L as a non-virtual base class.

Since E has L as a virtual base that's what gets created first.
Avatar of Pra Sys

ASKER

Is there any easy way to remember these rules? :)
unfortunately no easy way!
Avatar of Pra Sys

ASKER

Do you know the specific reason behind constructing virtual base classes ahead of everything else?? I mean what problems it avoids and what is the advantage?? I tried looking but didnt find anything convincing yet.
Avatar of Pra Sys

ASKER

Also in the example that I have given above, the most derived object is E. Will it really have 2 L objects - one from C (virtual base class) and one from D (direct base class)??
In this case I also get a warning as virtual base L is inaccessible in E due to ambiguity. Does that mean, only L part of D is visible or accessible to E? Incidently, virtual base L was the first thing that was got created!!
If I make L virtual base class for D, this warning goes away and I see only one L object as expected by standard. Is there any advantage of having direct inheritance instead of virtual inheritance in the middle of hierarchy like in this example?
SOLUTION
Avatar of phoffric
phoffric

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
Avatar of Pra Sys

ASKER

When i said middle I was referring to class D inheriting directly from L instead of L being its virtual base class.
>>>> the most derived object is E. Will it really have 2 L objects
Yes it will because D is publicly deriving from L and E is deriving from D in addition to deriving from C whose virtual base is L.

You have designed your class D to inherit publicly from L not virtually. I don't see it being a virtual base class of D.
Also why do you want L to be a virtual base class of D?

Virtual base classes exist for a reason. A classic pedagogical example is the diamond problem and there are practical situations where such class hierarchies exist. If you haven't heard of it I recommend, getting a good primer on C++ and inheritance and getting your fundamentals up to speed.

You just don't use virtual base classes randomly.
Avatar of Pra Sys

ASKER

Ok. Is there any specific reason behind constructing virtual base classes ahead of everything else by breaking the order of inheritance??
Where is the order of inheritance broken?
Avatar of Pra Sys

ASKER

@phorric
If I change my code so that class D inherits from L virtually, how will your class diagram look like? Like below??


                   ____L
                  |    / \
                  |   /   \
                  |  A    B
                  |   \    /
                  |    \  /
                  D     C
                   \   /
                    \ /
                     E
Avatar of Pra Sys

ASKER

@trinitrotoluene
When I said breaking order of inheritance, I meant virtual base class L of C was constructed ahead of construction of D even though D appears first in inheritance list. I understand now that this is by standard as per your answer above. I was looking for specifics behind such behavior. Why allow such thing?
SOLUTION
Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
>>>>If I change my code so that class D inherits from L virtually, how will your class diagram look like?

you will now see the virtual base class being constructed just once at the very beginning.


Like I said before there are 2 copies of L being constructed in your original program and this is because D is deriving publicly from L and not virtually.

As I said before you need to have a rationale behind your design. Check my previous comment on the need for virtual bases.

class L {
public:
	L(){
		cout << "L constructed" << endl;
	}
	~L(){
		cout << "L destructed" << endl;
	}
};

class A : public virtual L {
public:
	A(){
		cout << "A constructed" << endl;
	}
	~A(){
		cout << "A destructed" << endl;
	}
};

class B : public virtual L {
public:
	B(){
		cout << "B constructed" << endl;
	}
	~B(){
		cout << "B destructed" << endl;
	}
};

class C : public A, public B {
public:
	C(){
		cout << "C constructed" << endl;
	}
	~C(){
		cout << "C destructed" << endl;
	}
};

class D : virtual public L  {
public:
	D(){
		cout << "D constructed" << endl;
	}
	~D(){
		cout << "D destructed" << endl;
	}
};

class E : public D, public C{
public:
	E(){
		cout << "E constructed" << endl;
	}
	~E(){
		cout << "E destructed" << endl;
	}
};

int main()
{
	E e;
	return 0;
}

Open in new window

Avatar of Pra Sys

ASKER

This is how I understand above. Please correct me if I am wrong -

1.

Compiler checks the inheritance list for E.

2.

Finds E is derived from D and C in that order.

3.

It checks how D is derived and if it has any virtual base class. In this case there is L. So it goes ahead constructs L for D.

4.

It then checks how C is derived and if it has any virtual base class. In this there is L. But it is already constructed as part of D. So compiler moves on without doing anything.

5.

It constructs D.

6.

It constructs other base classes of C e.g A and B.

7.

It then constructs C.

8.

It points to copy of L from C as L is virtual base class for C (thru A and B).

9.

It constructs E.
SOLUTION
Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
Avatar of Pra Sys

ASKER

I guess all these get set methods of different classes invoked by E object are working on just one copy of bar. So value of bar changes fro 2 to 3 to 5.
SOLUTION
Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
Avatar of Pra Sys

ASKER

understood. But isn't that warning contradicts with the behavior when we have members in the class? Warning says L is inaccessible and then on the other hand we get error after adding members which means it "is accessible". or am I misinterpreting something here?
Avatar of Pra Sys

ASKER

So, compiler blocks access to virtual base class if another copy of same class is available directly?? So it prefers direct copy as long as we are not doing anything in the class. You also said in one of your post above (ID: 40307976) that at times two distinct copies of bar can be desired by designer in derived class. If it happens so, how do we achieve that without getting this error?? Is it achievable?
Avatar of phoffric
phoffric

I tried on three compilers and never got a warning. Warning and error messages are not always well-worded. Maybe you are using some compiler flags to provide more enforcement of good coding.

>> Warning says "virtual base L is inaccessible in E due to ambiguity"
I can see how that would be unclear from your OP code. Better wording might be
"If you add data members to L and try to access those members, then the compiler will not know which data member to access due to ambiguity."

To keep things simpler than what I posted, you can add to L a public data member, bar, to your OP code. That alone will not cause an error since you are not accessing it.

Then, in main, just add: e.bar = 1;
Now you will get an error.

>> we get error after adding members which means it "is accessible".
Again, adding members does not cause the error (and in my case, not even a warning).
But, in main, when I try to access bar, then I get an error because it is inaccessible due to ambiguity.
Avatar of Pra Sys

ASKER

ok. Thanks for answering that. I have mingw gcc 64 bit compatible compiler as you already know. it probably has this warning feature.
I installed MingGW recently.

Test>g++ --version
g++ (i686-posix-dwarf-rev0, Built by MinGW-W64 project) 4.9.1
Copyright (C) 2014 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

Test>g++ virtualBase.cpp
(no warnings)

Then I add in e.bar = 1;
Test>g++ virtualBase.cpp
virtualBase.cpp: In function 'int main()':
virtualBase.cpp:69:7: error: request for member 'bar' is ambiguous
     e.bar = 1;
       ^
virtualBase.cpp:13:9: note: candidates are: int L::bar
     int bar;
         ^
virtualBase.cpp:13:9: note:                 int L::bar
virtualBase.cpp:13:9: note:                 int L::bar

Open in new window

what command line did you use? any flags?
>> If it happens so, how do we achieve that without getting this error?? Is it achievable?
Just saw this post.

The code posted in http:#a40307411 shows the case of two bars with this class diagram:
        L
       / \
      A   B
      |  /
 L    | /
  \   |/
   D  C
    \/
     E

Open in new window

If you run that code, you will see that you can set and recover two different bar values.
Avatar of Pra Sys

ASKER

ok. By calling the 1st immediate base class methods. In this case , A and D. Thanks.
Sure thing. Hope it is clearer now.

>>  By calling the 1st immediate base class methods. In this case , A and D.
Well, that's not a rule. You can design it to use C instead of A, since from C's point of view, there is only one bar. C-A-L-B makes up that dreaded diamond of death which you took care of by making L a virtual base class of A and B.
Avatar of Pra Sys

ASKER

Blood Diamond :). Thanks for all the help!!!
I wonder why you got a warning in your OP code, and I did not.
My MinGW version
Test>g++ --version
 g++ (i686-posix-dwarf-rev0, Built by MinGW-W64 project) 4.9.1

Could you give me your command line with all the flags so that I can get that warning.
Avatar of Pra Sys

ASKER

I have enabled Wextra flag which is giving me that warning. Without that flag I dont get any warning.

Here is my compiler command command -

x86_64-w64-mingw32-g++.exe -Wall -fexceptions  -Wextra  -Wall -pg -g    -c C:\CPP\VirtualBaseClass2\main.cpp -o obj\Debug\main.o


C:\CPP\VirtualBaseClass2\main.cpp:103:7: warning: virtual base 'L' inaccessible in 'E' due to ambiguity [-Wextra]
 class E : public D, public C{
       ^
-Wextra --  Yes that works. Thanks.