Solved

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

Posted on 2014-09-05
31
327 Views
Last Modified: 2014-09-08
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.
0
Comment
Question by:mumbaikar
  • 15
  • 9
  • 7
31 Comments
 
LVL 12

Accepted Solution

by:
trinitrotoluene earned 200 total points
Comment Utility
Short answer : The virtual base class is constructed first.

From the standard the following set of rules is applied recursively:

- First, the most derived class's constructor calls the constructors of the virtual base class subobjects. Virtual base classes are initialized in depth-first, left-to-right order.

- Next, direct base class subobjects are constructed in the order they are declared in the class definition.

- Next, (nonstatic) member subobjects are constructed, in the order they were declared in the class definition.

- Finally, the body of the constructor is executed.

So in this case E is your most derived class and it calls its virtual base class constructor first. Remove D's inheritance from L and this will become clearer to you.
0
 
LVL 12

Expert Comment

by:trinitrotoluene
Comment Utility
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.
0
 

Author Comment

by:mumbaikar
Comment Utility
Is there any easy way to remember these rules? :)
0
 
LVL 12

Expert Comment

by:trinitrotoluene
Comment Utility
unfortunately no easy way!
0
 

Author Comment

by:mumbaikar
Comment Utility
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.
0
 

Author Comment

by:mumbaikar
Comment Utility
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?
0
 
LVL 32

Assisted Solution

by:phoffric
phoffric earned 300 total points
Comment Utility
You have a smart compiler that gave you a warning. My two compilers did not give a warning. If you actually did something, you would get an error. Probably the warning is trying to warn you of that possibility. Here is something for you to experiment with if you want. BTW - when you said "middle", I didn't know which middle you were referring to. Then again, I've been up for 22 hours.
#include <iostream>

using namespace std;

class L {
public:
    L(){
        cout << "L constructed" << endl;
    }
    ~L(){
        cout << "L destructed" << endl;
    }
    int lget() {return bar;}
    void lset(int in) {bar = in;}

private:
   int bar;
};

class A : public virtual L {
public:
    A(){
        cout << "A constructed" << endl;
    }
    ~A(){
        cout << "A destructed" << endl;
    }
    int aget() {return L::lget();}
    void aset(int in) {L::lset(in);}
};

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;
    }
    int dget() {return L::lget();}
    void dset(int in) {L::lset(in);}
};

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

int main()
{
    E e;
//  e.lset(2); // ambiguous; Which bar are you setting?
    e.aset(3);
    e.dset(5);
    int x = e.aget();
    int y = e.dget();
    return 0;
}
/*
        L
       / \
      A   B
      |  /
 L    | /
  \   |/
   D  C
    \/
     E
*/

Open in new window

0
 

Author Comment

by:mumbaikar
Comment Utility
When i said middle I was referring to class D inheriting directly from L instead of L being its virtual base class.
0
 
LVL 12

Expert Comment

by:trinitrotoluene
Comment Utility
>>>> 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.
0
 

Author Comment

by:mumbaikar
Comment Utility
Ok. Is there any specific reason behind constructing virtual base classes ahead of everything else by breaking the order of inheritance??
0
 
LVL 12

Expert Comment

by:trinitrotoluene
Comment Utility
Where is the order of inheritance broken?
0
 

Author Comment

by:mumbaikar
Comment Utility
@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
0
 

Author Comment

by:mumbaikar
Comment Utility
@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?
0
 
LVL 12

Assisted Solution

by:trinitrotoluene
trinitrotoluene earned 200 total points
Comment Utility
That really doesn't amount to breaking the inheritance hierarchy.

So in your example you have the "dreaded diamond" hierarchy. L, A, B, and C. If you didn't use a virtual base class you would end up having multiple copies of the L.

And in a practical scenario L wouldn't be empty. It would have some data and functions. So does C inherit A's copy of L or B's copy of L.
Virtual base classes avoid this ambiguity and ensure just one copy.
Naturally they need to be created first.

Since E derives from C the standard requires that a VBC is constructed first.
0
 
LVL 12

Expert Comment

by:trinitrotoluene
Comment Utility
>>>>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

0
Better Security Awareness With Threat Intelligence

See how one of the leading financial services organizations uses Recorded Future as part of a holistic threat intelligence program to promote security awareness and proactively and efficiently identify threats.

 

Author Comment

by:mumbaikar
Comment Utility
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.
0
 
LVL 32

Assisted Solution

by:phoffric
phoffric earned 300 total points
Comment Utility
>> If I change my code so that class D inherits from L virtually, how will your class diagram look like? Like below??
    Yes, but there should be some notation indicating when a virtual base is used. I just added a comment in the class diagram.

>> If I make L virtual base class for D, this warning goes away and I see only one L object as expected by standard.
   Ok, you understand when you get one instance of L vs. more than one. If you think about that point, that should help you dictate when you need virtual base classes. Maybe there is some case where you actually want two L::bar distinct from each other; maybe not. By making L a virtual base class of D, my previous code post changes its functionality significantly. Plus you can do extra things as there is no longer ambiguity. Here is the revised code and comments indicate the functional changes.  Aside from making L a virtual base class of D, the only changes are in the main() function.
/*
    +---L        // L now virtual base to D, A, B
   /   / \
  /   A   B
 /    |  /
 |    | /
  \   |/
   D  C
    \/
     E
*/
#include <iostream>

using namespace std;

class L {
public:
    L(){
        cout << "L constructed" << endl;
    }
    ~L(){
        cout << "L destructed" << endl;
    }
    int lget() {return bar;}
    void lset(int in) {bar = in;}

private:
   int bar;
};

class A : public virtual L {
public:
    A(){
        cout << "A constructed" << endl;
    }
    ~A(){
        cout << "A destructed" << endl;
    }
    int aget() {return L::lget();}
    void aset(int in) {L::lset(in);}
};

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 virtual L {
public:
    D(){
        cout << "D constructed" << endl;
    }
    ~D(){
        cout << "D destructed" << endl;
    }
    int dget() {return L::lget();}
    void dset(int in) {L::lset(in);}
};

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

int main()
{
    E e;
    e.lset(2); // no longer ambiguous; only one bar that is set
    int w = e.lget(); // and we get that bar - no ambiguity
    e.aset(3);
    e.dset(5);  // overwrites the 3 with the 5 since only one bar
    int x = e.aget();
    int y = e.dget();
    return 0;
}

Open in new window

0
 

Author Comment

by:mumbaikar
Comment Utility
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.
0
 
LVL 32

Assisted Solution

by:phoffric
phoffric earned 300 total points
Comment Utility
>> So value of bar changes fro 2 to 3 to 5.
Right, when all the classes inherit L as a virtual base class, then there is only one bar, and the sets just overwrite the bar value.

line 88 now works: e.lset(2);
In post http:#a40307411 if you just looked at the code (forget about the compiler error for a moment), and you tried to figure out what e.lset(2)  would do, you would have a dilemma. Sure, E inherits lset() from L, but from which L. In that post, there are two L's constructed. The compiler handles this dilemma by issuing an error and refusing to build the program.

When you got your warning, it built a program, but there wasn't really any dilemma since you were not really doing anything - you had no data members in L that could cause any confusion.
0
 

Author Comment

by:mumbaikar
Comment Utility
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?
0
 

Author Comment

by:mumbaikar
Comment Utility
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?
0
 
LVL 32

Expert Comment

by:phoffric
Comment Utility
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.
0
 

Author Comment

by:mumbaikar
Comment Utility
ok. Thanks for answering that. I have mingw gcc 64 bit compatible compiler as you already know. it probably has this warning feature.
0
 
LVL 32

Expert Comment

by:phoffric
Comment Utility
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?
0
 
LVL 32

Expert Comment

by:phoffric
Comment Utility
>> 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.
0
 

Author Comment

by:mumbaikar
Comment Utility
ok. By calling the 1st immediate base class methods. In this case , A and D. Thanks.
0
 
LVL 32

Expert Comment

by:phoffric
Comment Utility
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.
0
 

Author Comment

by:mumbaikar
Comment Utility
Blood Diamond :). Thanks for all the help!!!
0
 
LVL 32

Expert Comment

by:phoffric
Comment Utility
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.
0
 

Author Comment

by:mumbaikar
Comment Utility
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{
       ^
0
 
LVL 32

Expert Comment

by:phoffric
Comment Utility
-Wextra --  Yes that works. Thanks.
0

Featured Post

How your wiki can always stay up-to-date

Quip doubles as a “living” wiki and a project management tool that evolves with your organization. As you finish projects in Quip, the work remains, easily accessible to all team members, new and old.
- Increase transparency
- Onboard new hires faster
- Access from mobile/offline

Join & Write a Comment

Suggested Solutions

Written by John Humphreys C++ Threading and the POSIX Library This article will cover the basic information that you need to know in order to make use of the POSIX threading library available for C and C++ on UNIX and most Linux systems.   [s…
This article will show you some of the more useful Standard Template Library (STL) algorithms through the use of working examples.  You will learn about how these algorithms fit into the STL architecture, how they work with STL containers, and why t…
The goal of the video will be to teach the user the difference and consequence of passing data by value vs passing data by reference in C++. An example of passing data by value as well as an example of passing data by reference will be be given. Bot…
The viewer will learn how to pass data into a function in C++. This is one step further in using functions. Instead of only printing text onto the console, the function will be able to perform calculations with argumentents given by the user.

744 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question

Need Help in Real-Time?

Connect with top rated Experts

19 Experts available now in Live!

Get 1:1 Help Now