Last chapter, in lesson 17.9 -- Multiple inheritance, we left off talking about the “diamond problem”. In this section, we will resume this discussion.
Note: This section is an advanced topic and can be skipped or skimmed if desired.
The diamond problem
Here is our example from the previous lesson (with some constructors) illustrating the diamond problem:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 |
class PoweredDevice { public: PoweredDevice(int power) { std::cout << "PoweredDevice: " << power << '\n'; } }; class Scanner: public PoweredDevice { public: Scanner(int scanner, int power) : PoweredDevice{ power } { std::cout << "Scanner: " << scanner << '\n'; } }; class Printer: public PoweredDevice { public: Printer(int printer, int power) : PoweredDevice{ power } { std::cout << "Printer: " << printer << '\n'; } }; class Copier: public Scanner, public Printer { public: Copier(int scanner, int printer, int power) : Scanner{ scanner, power }, Printer{ printer, power } { } }; |
Although you might expect to get an inheritance diagram that looks like this:
If you were to create a Copier class object, by default you would end up with two copies of the PoweredDevice class -- one from Printer, and one from Scanner. This has the following structure:
We can create a short example that will show this in action:
1 2 3 4 5 6 |
int main() { Copier copier{ 1, 2, 3 }; return 0; } |
This produces the result:
PoweredDevice: 3 Scanner: 1 PoweredDevice: 3 Printer: 2
As you can see, PoweredDevice got constructed twice.
While this is often desired, other times you may want only one copy of PoweredDevice to be shared by both Scanner and Printer.
Virtual base classes
To share a base class, simply insert the “virtual” keyword in the inheritance list of the derived class. This creates what is called a virtual base class, which means there is only one base object. The base object is shared between all objects in the inheritance tree and it is only constructed once. Here is an example (without constructors for simplicity) showing how to use the virtual keyword to create a shared base class:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
class PoweredDevice { }; class Scanner: virtual public PoweredDevice { }; class Printer: virtual public PoweredDevice { }; class Copier: public Scanner, public Printer { }; |
Now, when you create a Copier class object, you will get only one copy of PoweredDevice per Copier that will be shared by both Scanner and Printer.
However, this leads to one more problem: if Scanner and Printer share a PoweredDevice base class, who is responsible for creating it? The answer, as it turns out, is Copier. The Copier constructor is responsible for creating PoweredDevice. Consequently, this is one time when Copier is allowed to call a non-immediate-parent constructor directly:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 |
#include <iostream> class PoweredDevice { public: PoweredDevice(int power) { std::cout << "PoweredDevice: " << power << '\n'; } }; class Scanner: virtual public PoweredDevice // note: PoweredDevice is now a virtual base class { public: Scanner(int scanner, int power) : PoweredDevice{ power } // this line is required to create Scanner objects, but ignored in this case { std::cout << "Scanner: " << scanner << '\n'; } }; class Printer: virtual public PoweredDevice // note: PoweredDevice is now a virtual base class { public: Printer(int printer, int power) : PoweredDevice{ power } // this line is required to create Printer objects, but ignored in this case { std::cout << "Printer: " << printer << '\n'; } }; class Copier: public Scanner, public Printer { public: Copier(int scanner, int printer, int power) : PoweredDevice{ power }, // PoweredDevice is constructed here Scanner{ scanner, power }, Printer{ printer, power } { } }; |
This time, our previous example:
1 2 3 4 5 6 |
int main() { Copier copier{ 1, 2, 3 }; return 0; } |
produces the result:
PoweredDevice: 3 Scanner: 1 Printer: 2
As you can see, PoweredDevice only gets constructed once.
There are a few details that we would be remiss if we did not mention.
First, virtual base classes are always created before non-virtual base classes, which ensures all bases get created before their derived classes.
Second, note that the Scanner and Printer constructors still have calls to the PoweredDevice constructor. When creating an instance of Copier, these constructor calls are simply ignored because Copier is responsible for creating the PoweredDevice, not Scanner or Printer. However, if we were to create an instance of Scanner or Printer, those constructor calls would be used, and normal inheritance rules apply.
Third, if a class inherits one or more classes that have virtual parents, the most derived class is responsible for constructing the virtual base class. In this case, Copier inherits Printer and Scanner, both of which have a PoweredDevice virtual base class. Copier, the most derived class, is responsible for creation of PoweredDevice. Note that this is true even in a single inheritance case: if Copier was singly inherited from Printer, and Printer was virtually inherited from PoweredDevice, Copier is still responsible for creating PoweredDevice.
Fourth, all classes inheriting a virtual base class will have a virtual table, even if they would normally not have one otherwise, and thus be larger by a pointer.
Because Scanner and Printer derive virtually from PoweredDevice, Copier will only be one PoweredDevice subobject. Scanner and Printer both need to know how to find that single PoweredDevice subobject, so they can access its members (because after all, they are derived from it). This is typically done through some virtual table magic (which essentially stores the offset from each subclass to the PoweredDevice subobject).
![]() |
![]() |
![]() |
For the very first example you didn't use 'std::cout' .
I was wondering why you didn't create variable 'copier' for the Copier constructer and the body is empty.
>>Fourth, all classes inheriting a virtual base class will have a virtual table, even if they would normally not have one otherwise, and thus be larger by a pointer.
Does that sentence mean although printer and scanner share a virtual base class they have a virtual table by the pointer would be larger for them?
>>Scanner and Printer both need to know how to find that single PoweredDevice subobject, so they can access its members (because after all, they are derived from it).
What does 'its' pronoun refer to? the PowerDevice?
The magic in placing into vtable is pretty simple, according to "Inside C++ Object Model" chapter 3 by S.Lippman, - offsets of a kind (Printer/Scanner subobj beginning distance to PoweredDevice subobj beginning) are just placed into vtable with negative indices (while virtual function slots have 0 and positive ones).
When a Copier is created, it's layout is already known, offset to PoweredDevice is at (this->__vptr__Copier[-1]), this way it's members can be accessed at runtime.
The whole magical mechanic is needed because at compile time *usage* like
poses a problem: compiler does not know the actual type of p or s, so it can't calculate offsets to powered_device_member-s to increment them. PoweredDevice piece of layout is lost somewhere in between of Printer or Scanner layout. All we know is the location of powered_device_member inside PoweredDevice. But it knows the layout and thus offsets for each individual class, so it just needs a uniform way to get these offsets, having only a pointer to some class object. Fortunately, knowing a pointer to a class obj, you also know it's vptr location (compiler places it in the beginning or end of class obj and knows it), so you can access vtable through vptr and take this precomputed offset from there.
In multiple virtual base classes case, compiler places similar offsets for all of virtual bases and takes them in the same manner, because at the place of a call it knows which of the virtual bases' interface is called and can correlate it to the order in which it placed the precomputed offsets into approppriate vtable, hence index in it.
Example. To get powered_device_member knowing p, we go to location of vptr inside PoweredDevice subobject (most commonly it's in the very beginning of class layout), then go to vtable, which is really a Printer's vtable, cause that's where Printer's vptr point to. Then we go to the list of virtual bases and find PoweredDevice there. At this point we know we are searching for PoweredDevice's member and we have some hash of a PD's class name, which can be used as token for indexing in vtable. And by it we get PD subobj's offset inside Printer object.
What is constructor in derived class
Why a virtual base class is called so?
Because it's resolved through virtual table, I think.
Hello All,
Can anyone explain me the last paragraph in much simpler words :
"Because Scanner and Printer derive virtually from PoweredDevice, Copier will only be one PoweredDevice subobject. Scanner and Printer both need to know how to find that single PoweredDevice subobject, so they can access its members (because after all, they are derived from it). This is typically done through some virtual table magic (which essentially stores the offset from each subclass to the PoweredDevice subobject)."
I've posted a comment above which clarifies the magical part a bit.
One interesting thing to point out: if the virtual base class constructor is neglected from the most-derived class's constructor initializer list then the base class's default constructor will be implicitly called. Here is code demonstrating this, where a 'grandpa constructor' is printed twice. The first print is from the implicit call by constructor Child::Child() to Grandpa::Grandpa(), while the second print is from the non-virtually inheriting Parent1, whose constructor calls the second constructor Grandpa::Grandpa(int).
I went down this route since it wasn't clear if a 'shared' subobject is only shared between virtual inheritors of a class, or if a virtual inheritor could share a subobject with a normal inheritor. Clearly only virtual inheritors can share the same subobject.
It prints:
grandpa constructor
gandpa constructor2 42
parent1 constructor
parent2 constructor
child constructor
child
Hi Nascardriver/Alex,
Could you please explain output of the code shared by koe above?
I am getting the same result.
But, if I skip virtual base class constructor in your example, I get compilation error.
error: no matching function for call to 'PoweredDevice::PoweredDevice()'
Scanner{ scanner, power }, Printer{ printer, power }
What would happen to Copier if you only made Scanner virtually inherit PoweredDevice?
Does whether a base class is virtual or not entirely depend on if the derived class decides to virtually inherit it? That seems a little weird.
What if I wanted to make another class which inherits Scanner and Printer but doesn't want to virtually inherit PoweredDevice?
Hi Alex,
As you mentioned that, in this case, both Scanner and Printer class share the base class PoweredDevice variables, so does it mean that if we try to change the value of variable that is shared among the three classes through Scanner class object, then that same change will be reflected in the Printer class as well as PoweredDevice class??
P.S. Although I tried to verify this by coding it, but it didn't work. Can you please help me in explaining the reason behind it?
If `PoweredDevice` has a member variable, that variable is accessible in `Scanner`, `Printer` and `Copier`. But this variable exists only once per object (If we're talking about a `Copier`, this object is a `Copier`, `Printer`, `Scanner`, and `PoweredDevice` at the same time). If you modify it, and for example to `Scanner` portion accesses the variable, it sees the modified variable.
Hey!
For completion's sake, maybe add that instantiating multiple Copier objects will construct a PoweredDevice per Copier, took me a couple minutes to grasp that it's merely overriding all it's derived base class's(scanner and printer's) virtual constructor calls and calling it itself.
Making more than one Copier illustrates a separate base class call. For me, the line that caused my confusion was: "This creates what is called a virtual base class, which means there is only one base object that is shared" , which led me to think it was some sort of static base.
Just a suggestion, do with it as you like,
Cheers,
Cakes
Lesson amended to make clear that the base is not shared between distinct object, thanks for the suggestion!
Hi Alex,
How about situation which create Printer class and Scanner class share the same PowerDevice object? In this case there is no Copier class, is it still possible?
//Btw, this lesson is 12.7 but page url is 128 (https://www.learncpp.com/cpp-tutorial/128-virtual-base-classes/)
Hi Susano!
When you create a `Printer` or `Scanner` separately, they'll each have their own `PoweredDevice`.
> this lesson is 12.7 but page url is 128
Thanks for pointing it out. The urls aren't updated so that links to the lesson continue to work. When a lesson's title is changed, the url stays the same.
Hello Alex
Now, when you create a Copier class, you will get only one copy of PoweredDevice that will be shared by both Scanner and Printer.
isn't ...a Copier object instead of Copier class ??
Yep. Fixed, thanks!
In the following code, I do not understand the comment:
"// this line is required to create Scanner objects, but ignored in this case" on line 16 and same for line 26.
Why is it not ignored in the previous example (without virtual base class)?
Code:
Thanks!
In the previous example there's one `PoweredDevice` as the base of `Printer` and one as the base of `Scanner`.
When they inherit virtually, there's only one `PoweredDevice` for both. This `PoweredDevice` gets constructed by `Copier`, so the calls in `Scanner` and `Printer` have no effect.
Ok, so
instead "// this line is required to create Printer objects, but ignored in this case"
it should be
"// this line is required to create 'PoweredDevice' objects, but ignored in this case" , right?
Kind of. The `PoweredDevice` is constructed as a part of the `Printer` or `Scanner`. The line is used when you construct a `Printer` or `Scanner`. It's unused when the `Printer` or `Scanner` is created during construction of a `Copier`.
this code show some error in dev c++
What kind of error?
Typo:
Because Scanner and Printer derive virtually from PoweredDevice, Copier will only >>>be<<< one PoweredDevice subobject.
I think we can say this?:
Classes themselves can inherit virtually, which means that construction of the base subobject is effectively kicked down the road to the most derived class. Within the definition of a class that inherits virtually from a base class, any calls to the constructor of the virtual base class will simply be ignored. For example, this addresses the 'diamond problem': (etc..)
I'd edit what I wrote slightly:
"Classes themselves can inherit virtually, which means that construction of the base subobject is effectively kicked down the road to the most derived class. Within the definition of a class that inherits virtually from a base class, any calls to the constructor of the virtual base class will simply be ignored (but must be present!). For example, this addresses the 'diamond problem': (etc..)"
"if Copier was singly inherited from Printer, and Printer was virtually inherited from PoweredDevice,"
Perhaps "if Copier singly inherits from Printer, and Printer virtually inherits from PoweredDevice," is a little clearer?
I know, it's a trifle
the above code doesn't work with TurboC++ what i have to do for this problem please suggest me
Upgrade to a modern compiler.
I tried writing whole program by only having default constructor( No parameters). In that case, PoweredDevice constructor call from Copier constructor was not included and it was giving expected results. How is that possible?
I don't see that behavior. Can you post your code?
I think that the url is wrong.
.../cpp-tutorial/128-virtual-base-classes/
Should not it be 127?
It should be. This lesson is flagged for a cleanup, so I'll fix that when I rewrite this lesson. Thanks for pointing this out.
"So in order to facilitate this, the compiler creates a virtual table for each class directly inheriting the virtual class (Printer and Scanner). These virtual tables point to the functions in the most derived class." , the virtual base is considered a direct base of most derived class, so why the virtual table is set for each class deriving from the virtual base pointing to the most derived, i thought that the connection between the virtual base and its derived are lost so how a virtual table between in these derived pointing to the most derived would help ?
I rewrote the last paragraph of this article -- have a read and see if that is more understandable.
Yes i understand it now, thanks a lot :)
Hi Alex!
The call to the @PoweredDevice constructor should be moved before the ones to @Scanner and @Printer.
Your order produces warnings
warning: base class 'Printer' will be initialized after base 'PoweredDevice' [-Wreorder]
warning: base class 'Scanner' will be initialized after base 'PoweredDevice' [-Wreorder]
Updated. Thanks for pointing this out.
Hi Alex, there's one thing I don't understand.
In constructors in derived class where we need to instantiate Base class there is some difference in initialization.
For example, if you try to initialize Scanner, Printer and PoweredDevice in Copier in uniform manner (Like Scanner{scanner, power}, Printer{printer, scanner}, etc.) the behaviour is not the same as Scanner(scanner, power), Printer(printer, power), etc.
I checked chapter "2.1 — Fundamental variable definition, initialization, and assignment" where it says Rule: If you’re using a C++11 compatible compiler, favor uniform initialization
And additional I checked 8.5 "Constructors" and where it says Rule: Use direct or uniform initialization with your classes.
+ "8.5a — Constructor member initializer lists" the same Rule: Favor uniform initialization over direct initialization if your compiler is C++11 compatible. Actually I didn't find any strings where you're using uniform initialization for to instantiate base class.
+ 11.4 — Constructors and initialization of derived classes - as I see you haven't mentioned uniform initialization at all.
Could you describe this difference a little bit clearly. Thank you.
P.S. I use CodeBlocks 17.2 on Debian 9.
Hi Viktar!
I'm unable to reproduce what you described. Both
and
(should) produce the same results. Did you do anything other than that? If so, what? If no, which compiler are you using? Make sure you're running it's newest version.
> Actually I didn't find any strings where you're using uniform initialization
Alex doesn't use uniform initialization himself, because he's old :). You should do so.
If I use this code
then result is expected.
In the same time when I use this code
I've got next result :
PoweredDevice: 3
PoweredDevice: 3
Scanner: 1
PoweredDevice: 3
Printer: 2
The compiler version is: gcc (Debian 6.3.0-18+deb9u1) 6.3.0 20170516
Looks like gcc is quite old
It looks like @PoweredDevice is getting called from all three @Printer, @Scanner and @Copier, which should not be happening, because only the call to @PoweredDevice from @Copier has an effect on the resulting object. It could be that the standard allows the behavior you're experiencing, nonetheless I suggest you to update g++ to get consistent results. g++ 8.2.1 produces the same output no matter which initialization is used.
Hi, did you resolve this? I have the same problem, code::blocks 17.12, C++17 compiler flag set, windows 10.