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.
James BondSoftware ProfessionalAsked:
Who is Participating?
I wear a lot of hats...

"The solutions and answers provided on Experts Exchange have been extremely helpful to me over the last few years. I wear a lot of hats - Developer, Database Administrator, Help Desk, etc., so I know a lot of things but not a lot about one thing. Experts Exchange gives me answers from people who do know a lot about one thing, in a easy to use platform." -Todd S.

trinitrotolueneDirector - Software EngineeringCommented:
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

Experts Exchange Solution brought to you by

Your issues matter to us.

Facing a tech roadblock? Get the help and guidance you need from experienced professionals who care. Ask your question anytime, anywhere, with no hassle.

Start your 7-day free trial
trinitrotolueneDirector - Software EngineeringCommented:
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
James BondSoftware ProfessionalAuthor Commented:
Is there any easy way to remember these rules? :)
0
Cloud Class® Course: Microsoft Azure 2017

Azure has a changed a lot since it was originally introduce by adding new services and features. Do you know everything you need to about Azure? This course will teach you about the Azure App Service, monitoring and application insights, DevOps, and Team Services.

trinitrotolueneDirector - Software EngineeringCommented:
unfortunately no easy way!
0
James BondSoftware ProfessionalAuthor Commented:
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
James BondSoftware ProfessionalAuthor Commented:
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
phoffricCommented:
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
James BondSoftware ProfessionalAuthor Commented:
When i said middle I was referring to class D inheriting directly from L instead of L being its virtual base class.
0
trinitrotolueneDirector - Software EngineeringCommented:
>>>> 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
James BondSoftware ProfessionalAuthor Commented:
Ok. Is there any specific reason behind constructing virtual base classes ahead of everything else by breaking the order of inheritance??
0
trinitrotolueneDirector - Software EngineeringCommented:
Where is the order of inheritance broken?
0
James BondSoftware ProfessionalAuthor Commented:
@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
James BondSoftware ProfessionalAuthor Commented:
@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
trinitrotolueneDirector - Software EngineeringCommented:
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
trinitrotolueneDirector - Software EngineeringCommented:
>>>>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
James BondSoftware ProfessionalAuthor Commented:
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
phoffricCommented:
>> 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
James BondSoftware ProfessionalAuthor Commented:
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
phoffricCommented:
>> 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
James BondSoftware ProfessionalAuthor Commented:
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
James BondSoftware ProfessionalAuthor Commented:
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
phoffricCommented:
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
James BondSoftware ProfessionalAuthor Commented:
ok. Thanks for answering that. I have mingw gcc 64 bit compatible compiler as you already know. it probably has this warning feature.
0
phoffricCommented:
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
phoffricCommented:
>> 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
James BondSoftware ProfessionalAuthor Commented:
ok. By calling the 1st immediate base class methods. In this case , A and D. Thanks.
0
phoffricCommented:
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
James BondSoftware ProfessionalAuthor Commented:
Blood Diamond :). Thanks for all the help!!!
0
phoffricCommented:
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
James BondSoftware ProfessionalAuthor Commented:
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
phoffricCommented:
-Wextra --  Yes that works. Thanks.
0
It's more than this solution.Get answers and train to solve all your tech problems - anytime, anywhere.Try it for free Edge Out The Competitionfor your dream job with proven skills and certifications.Get started today Stand Outas the employee with proven skills.Start learning today for free Move Your Career Forwardwith certification training in the latest technologies.Start your trial today
C++

From novice to tech pro — start learning today.

Question has a verified solution.

Are you are experiencing a similar issue? Get a personalized answer when you ask a related question.

Have a better answer? Share it in a comment.