Virtual Inheritance: "dreaded triangle problem" and shared subobjects

I'm trying to grasp the role of virtual inheritance in multiple inheritance and polymorphism. I have three classes: A, B, and C. A is the base of B and C. B is the (abstract) base of C. I chose to call this the "dreaded triangle problem" because it involves only three classes, rather than four.

1
2
3
4
5
A
|   \
|     B (abstract)
|   /
C


A may be instantiated by itself when it is not a B. C may be instantiated, and it is an A and B. This part is key: B is an A -- B must inherit from A because B must use A's members and methods. Anything that is a B must also be an A.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class A {
public:
foo();
}

class B: public A {
public:
bar(); //depends on A::foo()
protected:
B(); //protected constructor (abstract class)
}

class C: public A, public B {
public:
baz();
}


The first solution that comes to mind is "C need not inherit from A, because it will indirectly inherit from A by virtue of inheriting from B." This may be acceptable for this very case, but what if C also inherits from another class which inherits from A? This might be a separate issue, because it would create a diamond problem (in contrast to the above illustrated "triangle problem").

Questions:
1. I understand that the keyword "virtual" preceding a base class declaration prevents a subobject from being created for that base class. What is the context of this rule? Does this mean all base classes, that are marked "virtual" along the hierarchy of the instantiated object's class, will share the same one subobject per class? What about non-virtual base classes, do each of those summon a unique subobject per base class declaration, in addition to each "shared" virtual-inherited base class?

2. In the example illustrated above, where should "virtual" appear?
class C: virtual public A, virtual public B?
class C: public A, virtual public B?
class B: virtual public A?
Something else?

3. Was I correct in guessing that C should not inherit directly from A? This could cause a diamond problem later on, but it may be an acceptable solution to the "triangle problem".
Last edited on
1) I can't give a complete answer on this question, as my experience with virtual inheritance is rather sparse. What I can say is that if A is derived from virtually, then it will have only one instance. Whereas if you do not derive from it virtually, each child will have its own instance.

What I can't tell you is what happens when you derive virtually with one child and non-virtually from another.


2) You want the inheritance from A to be virtual. So:
1
2
class B : virtual public A
class C : virtual public A, public B



3) I would say yes. If C is deriving from B, then it already derives from A. There's no need to derive from A again. A linear hierarchy of A->B->C would work just fine in this example, and would eliminate the need for virtual inheritance.
Thanks Disch. With the inheritance scheme you described, both solutions sound viable.

I don't know if there are any side-effects of virtually inheriting from A in both B and C (probably none). This raises another interesting question: why not use virtual inheritance everywhere? It seems that creating exclusive subobects (non-virtual) is a less common use-case than using shared subobjects (virtual). But perhaps that's a topic for another thread.

I sort of doubt the correctness of the single inheritance solution (A->B->C) in some specific cases (for example, when C is written as part of a sub-project or API extension, and the author of C is not supposed to rely on the fact that B inherits from A (for that is only an implementation detail only required because B uses A's methods, but that could change)). However, as long as C's author is allowed to assume that B is derived from A, then single inheritance might be the best pragmatic solution.

I'm still open to ideas, but I'll post a reply when I decide on something.
I sort of doubt the correctness of the single inheritance solution (A->B->C) in some specific cases (for example, when C is written as part of a sub-project or API extension, and the author of C is not supposed to rely on the fact that B inherits from A (for that is only an implementation detail only required because B uses A's methods, but that could change)).

But if that's the case, than someone, somewhere, has misunderstood what public inheritance represents. It represents the fact the B is an A. You can't ignore that B inherits from A, because to do so would be to ignore a fundamental, designed-in part of what B is. It would be like me trying to design a type of car, without being able to rely on the fact that a car is a vehicle.

If the fact that B inherits from A isn't fundamental to what B is and how it is supposed to be used, then it shouldn't be publicly inheriting from A at all.

Edit: Actually, I'm not sure how virtual inheritance affects non-public inheritance.

Then again, in the real world, the use of non-public inheritance is vanishingly rare - no matter how theoretically suited it is to modelling certain types of relationship.
Last edited on
I don't know if there are any side-effects of virtually inheriting from A in both B and C (probably none).


It mucks up the construction order.

With non-virtual inheritance, C calls B's ctor, and B calls A's ctor.
With virtual inheritance, C must call A's ctor directly, then call B's ctor. If B calls an A ctor, that call is discarded/ignored (I think... not 100% on this).

Destruction order might also be altered, but I'm not sure. I kind of doubt it.... logically, A would still have to be destructed last even if virtual.

This raises another interesting question: why not use virtual inheritance everywhere?


Apart from the mentioned ctor thing... there might be an ever-so-slight performance hit. But the main reason I avoid it is because it's a more complicated model, and usually isn't necessary. KISS.

the author of C is not supposed to rely on the fact that B inherits from A


MikeyBoy had a good reply to this.

The way I see it... A provides an interface. B also provides an interface.

Since B inherits from A... B's interface includes all of A's interface.

When C inherits from B... it gets all of B's interface. Since B includes A's interface, this means C also has A's interface. C doesn't have to know that it inherited A's interface, because as far as it knows/cares, it just inherited B's interface.

Hierarchies that have classes talking with ancestors 2 or 3 levels up the tree quickly become unwieldly.
MikeyBoy, You make a good point. I guess I was confusing virtual inheritance with the fundamental inheritance model. I've always used class Foo: public Bar; I haven't gotten into private inheritance or any other alternatives yet. As you said, I guess the is a relationship is beyond the scope of virtual inheritance. In that case, single inheritance would probably be acceptable (there is no reason for C to inherit directly from A).

Disch wrote:

It mucks up the construction order.

With non-virtual inheritance, C calls B's ctor, and B calls A's ctor.
With virtual inheritance, C must call A's ctor directly, then call B's ctor. If B calls an A ctor, that call is discarded/ignored (I think... not 100% on this).
Good to know. With simple, side-effect-free constructors, I don't see that causing much of a problem, but for more complex classes, that could definitely be an issue.

Disch wrote:
Since B inherits from A... B's interface includes all of A's interface.

That changes my understanding quite a lot. Previously I was thinking of inheritance in the model that it is represented in diagrams. That is, I was thinking of derived classes as conceptually "referencing" their base classes' members and methods, rather than "copying" them into their own class. (Conceptually -- I have no idea what's actually happening under the hood.)

My current understanding is as follows: virtual "If an object of this base class has been created while traversing the hierarchy of this object, reference it instead of creating a new copy. Otherwise create a new copy." With non-virtual inheritance, a new copy (subobject) is always created. Is this correct? Moreover (out of curiosity more than anything) does "this object" mean the one that was originally declared (ThisObject thisobject;) or the current point in the hierarchy (class BaseOfThisObject: public virtual BaseOfBase)?
That changes my understanding quite a lot. Previously I was thinking of inheritance in the model that it is represented in diagrams. That is, I was thinking of derived classes as conceptually "referencing" their base classes' members and methods, rather than "copying" them into their own class.


Your previous understanding is correct. My post was more conceptual than technical. In fact... your previous understanding was more technically correct than my conceptual example because it's closer to what is actually done.

But I find it's easier/better to think of this in a more abstract manner.

A simplistic example:

Mammal -> Dog -> Poodle

Mammal has a non-virtual "growFur" member function, since all mammals have fur.

Dog inherits from Mammal, therefore Dog also has a growFur function (even if it doesn't implement its own... it 'inherits' Mammal's function). Since all Mammals can grow fur... this means that all Dogs can grow fur.

Poodle inherits from Dog, so it gets Dog's growFur function (which just happens to be Mammal's growFur function). Since all Dogs can grow fur, this means that all Poodles can grow fur.

My current understanding is as follows: [snip] Is this correct?


Yes. Though like I said I don't know what happens when you derive from the same class with both virtual and non-virtual inheritance.


Moreover (out of curiosity more than anything) does "this object" mean the one that was originally declared (ThisObject thisobject;) or the current point in the hierarchy (class BaseOfThisObject: public virtual BaseOfBase)?


Virtual inheritance applies to the entire tree, not just each node. So I guess that would mean 'this object' is the object as it was declared.
Update: I've noticed a limitation with single inheritance: indirectly derived classes do not have access to indirect base class constructors. If C needs to use an A constructor of its own choosing, it must directly extend A (virtually or non-virtually). This is exemplified by the following error:
1
2
error: type 'A' is not a direct or virtual base of 'C'
C::C(): A() {} //C cannot construct A 


Without creating a dedicated A subobject for C, the only option left is
1
2
class B: virtual public A
class C: virtual public A, public B


This may have been obvious to others, but I thought this was worth noting.
Last edited on
Disch wrote:
Your previous understanding is correct. My post was more conceptual than technical. In fact... your previous understanding was more technically correct than my conceptual example because it's closer to what is actually done.
Disch wrote:
Virtual inheritance applies to the entire tree, not just each node. So I guess that would mean 'this object' is the object as it was declared.

Okay, so, from a conceptual perspective, every base class as declared is, after instantiation, a reference to some subobject of the instantiated object. Which referenced subobject depends upon whether or not the base was virtually inherited. Pertaining to
Disch wrote:
Yes. Though like I said I don't know what happens when you derive from the same class with both virtual and non-virtual inheritance.

my uneducated guess would be that all virtually inherited base classes will refer to the same subobject per class, and all non-virtually inherited base classes will refer to unique subobjects, regardless of the class.

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
class Mammal {
void growFur();
int amountOfFur;
}

class Dog: public Mammal {
void bark();
}

class Poodle: virtual public Dog {
//poodles don't shed
}

class Labrador: virtual public Dog {
void shed();
}

class Labradoode: public Poodle, public Labrador {

}

void Mammal::growFur() {
amountOfFur++;
}
 
void Labrador::shed() {
amountOfFur--;
}

There is one Mammal per Dog (non-virtual). Since there is only one Dog (virtual), Mammal::growFur() increments amountOfFur for the Dog. In other words Labradoodle::Poodle::Dog and Labradoodle::Labrador::Dog refer to the same subobject. This means that for a Labradodle, amountOfFur can be affected by Labrador::shed() and Mammal::growFur().

This all makes sense, except for one thing: who gets to construct Dog when Labradoodle is instantiated? Labrador and Poodle share the same Dog (virtual), so it would at first glance seem an arbitrary decision for which one calls Dog's constructor. This question is unanswered for the "triangle problem" as well: who chooses the constructor for A? B or C?
Last edited on
Okay, so, from a conceptual perspective, every base class as declared is, after instantiation, a reference to some subobject of the instantiated object. Which referenced subobject depends upon whether or not the base was virtually inherited.


Yes.

my uneducated guess would be that all virtually inherited base classes will refer to the same subobject per class, and all non-virtually inherited base classes will refer to unique subobjects, regardless of the class.


Could be. Like I say I don't know. =P
I almost never use virtual inheritance so I'm fuzzy on these kinds of edge-case details.

This all makes sense, except for one thing: who gets to construct Dog when Labradoodle is instantiated?


Labradoodle. Virtual parents have to be constructed first.

EDIT: more reading:

http://www.parashift.com/c++-faq/virtual-inheritance-ctors.html
Last edited on
I realize this is a bit of an edge case, and if I could, I would avoid it entirely, but the greatest hindrance in doing that is C must directly inherit from A because it has to use a specific constructor (not one chosen by B), but B must use members and methods of A. I could make B a Java-style interface (with no implementation) and thus it would not need to inherit from A, but this would lead to code duplication later on. On the other hand, B could provide all of the constructors of A on itself, but then B (and all of its siblings) would have to be updated every time A's constructors are changed in any way. I guess these are the woes of multiple inheritance.

It sounds like
1
2
class B: virtual public A
class C: virtual public A, public B

is the best option so far. Thanks to Disch and Mikey Boy, I've learned a lot from this thread and hopefully others will too. To summarize:
virtual means that any subobject instantiated from any virtual base class of the same name, anywhere along the hierarchy of the object being instantiated, will be referenced by the subobject that virtually inherits from its class.
A class may only use its parent's constructors if it is a direct child.
The object being instantiated is the one that constructs virtual parents (using the constructor of its own choosing). Virtual parents are constructed before non-virtual parents. Calls to constructors of virtually inherited base classes by classes other than the one being instantiated are ignored (I think?).
Virtual inheritance is not a substitute for single inheritance or private inheritance.
Topic archived. No new replies allowed.