You are on page 1of 5

Custom Search

Search

Starting out
Get the Ebook
Get Started with C or C++
Getting a Compiler
Book Recommendations

Tutorials
C Tutorial
C++ Tutorial
Java Tutorial
Game Programming
Graphics Programming
Algorithms & Data Structures
Debugging
All Tutorials

Practice
Practice Problems
Quizzes

Resources
Source Code
Source Code Snippets
C and C++ Tips
Finding a Job

References
Function Reference
Syntax Reference
Programming FAQ

Getting Help
Message Board
Email
About Us

Solving the Diamond Problem with Virtual Inheritance

By Andrei Milea

Multiple inheritance in C++ is a powerful, but tricky tool, that often leads to
problems if not used carefully. This article will teach you how to use virtual
inheritance to solve some of these common problems programmers run into. If you're
not familiar with multiple inheritance, check out this article on multiple
inheritance.

The diamond problem


One of the problems that arises due to multiple inheritance is the diamond problem.
A classical illustration of this is given by Bjarne Stroustrup (the creator of C++)
in the following example:
class storable //this is the our base class inherited by transmitter and receiver
classes
{
public:
storable(const char*);
virtual void read();
virtual void write();
virtual ~storable();
private:
....
}

class transmitter: public storable


{
public:
void write();
...
}

class receiver: public storable


{
public:
void read();
...
}

class radio: public transmitter, public receiver


{
public:
void read();
....
}
Since both transmitter and receiver classes are using the method write() from the
base class, when calling the method write() from a radio object the call is
ambiguous; the compiler can't know which implementation of write() to use, the one
from the transmitter class or the one from the receiver class.

To understand how this is works, let's take a look at how the objects are
represented in memory. Inheritance simply puts the implementation of two objects
one after another, but in this case radio is both a transmitter and a receiver, so
the storable class gets duplicated inside the radio object. The g++ compiler will
complain when compiling the code: error: 'request for member "write" is ambiguous',
because it can't figure out whether to call the method write() from
storable::receiver::radio or from storable::transmitter::radio.

Fortunately, C++ allows us to solve this problem by using virtual inheritance. In


order to prevent the compiler from giving an error we use the keyword virtual when
we inherit from the base class storable in both derived classes:
class transmitter: public virtual storable
{
public:
void read();
...
}

class receiver: public virtual storable


{
public:
void read();
...
}
When we use virtual inheritance, we are guaranteed to get only a single instance of
the common base class. In other words, the radio class will have only a single
instance of the storable class, shared by both the transmitter and receiver
classes. By having a single instance of storable, we've resolved the compiler's
immediate issue, the ambiguity, and the code will compile fine.
Memory Layout in Virtual Inheritance

In order to keep track of the single instance of the storable object, the compiler
will provide a virtual function table (vtable) for classes transmitter and
receiver. When a radio object is constructed, it creates one storable instance, a
transmitter instance and a receiver instance. The transmitter and receiver classes
have a virtual pointer in their vtables that stores the offset to the storable
class. When the transmitter class or the receiver class goes to access any fields
of the storable, it uses the virtual pointer in its vtable to find the storable
object and find the field in it. This tutorial offers a comprehensive explanation
of the memory layout of virtual inheritance in GCC.
Constructors and Virtual Inheritance

Because there is only a single instance of a virtual base class that is shared by
multiple classes that inherit from it, the constructor for a virtual base class is
not called by the class that inherits from it (which is how constructors are
called, when each class has its own copy of its parent class) since that would mean
the constructor would run multiple times. Instead, the constructor is called by the
constructor of the concrete class. In the example above, the class radio directly
calls the constructor for storable. If you need to pass any arguments to the
storable constructor, you would do so using an initialization list, as usual:
radio::radio ()
: storable( 10 ) // some value that storable needs
, transmitter()
, receiver()
{}
One thing to be aware of is that if either transmitter or receiver attempted to
invoke the storable constructor in their initialization lists, that call will be
completely skipped when constructing a radio object! Be careful, as this could
cause a subtle bug!

By the way, the constructors for virtual base classes are always called before the
constructors for non-virtual base classes. This ensures that a class inheriting
from a virtual base class can be sure the virtual base class is safe to use inside
the inheriting class's constructor.

The destructor order in a class hierarchy with a virtual base class follows the
same rules as the rest of C++: the destructors run in the opposite order of the
constructors. In other words, the virtual base class will be the last object
destroyed, because it is the first object that is fully constructed.
Delegating to a sister class

A powerful technique that arises from using virtual inheritance is to delegate a


method from a class in another class by using a common abstract base class. This is
also called cross delegation. Let's assume we have a similar scenario like in the
diamond example, with small changes. Suppose the write() method in transmitter
class needs to access the read() method from receiver for the radio to work (this
is kind of a weird behavior, but let's take it for the sake of illustration) :
class storable
{
public:
storable(const char*);
virtual void read()=0; //this becomes pure virtual making storable an
abstract
virtual void write(); //class
virtual ~storable();
private:
....
}

class transmitter: public virtual storable


{
public:
void write()
{
read();
....
}
}

class receiver: public virtual storable


{
public:
void read();
}

class radio: public transmitter, public receiver


{
public:
...
}

int main()
{
radio *rad = new radio();
receiver *r1 = rad;
transmitter *r2 =rad;

rad->write();
r1->write();
r2->write();
return 1;
}
Because of virtual inheritance, when the write() function from the transmitter
class is called, the method read() from the receiver class gets called (as you may
have noticed, the transmitter class doesn't have a read() function). In the above
hierarchy we can instantiate only the radio class because transmitter and receiver
are abstract due to virtual inheritance.
Other considerations when using multiple inheritance in C++

When you use a class that is based on virtual inheritance like radio, you should
avoid using C style casts and use the C++ specific dynamic_cast instead (more
information on casting). It will perform a runtime check for validity before
casting, so you can be sure that the type of the object you want to cast is related
(by inheritance) with the object type you want to cast into. If they are not
related, the result will be a NULL pointer or a bad_cast exception in case of
references.
Want to become a C++ programmer? The Cprogramming.com ebook, Jumping into C++,
will walk you through it, step-by-step. Get Jumping into C++ today!
Popular pages
Exactly how to get started with C++ (or C) today
C Tutorial
C++ Tutorial
5 ways you can learn to program faster
The 5 Most Common Problems New Programmers Face
How to set up a compiler
8 Common programming Mistakes
What is C++11?
How to make a game in 48 hours
Recent additions subscribe to a feed
How to create a shared library on Linux with GCC - December 30, 2011
Enum classes and nullptr in C++11 - November 27, 2011
Learn about The Hash Table - November 20, 2011
Rvalue References and Move Semantics in C++11 - November 13, 2011
C and C++ for Java Programmers - November 5, 2011
A Gentle Introduction to C++ IO Streams - October 10, 2011

Search
Google Custom Search

Join our mailing list to keep up with the latest news and updates about
Cprogramming.com!

Name

Email

Submit Form
Subscriber Counter

Advertising | Privacy policy | Copyright 1997-2017 Cprogramming.com. All rights


reserved. | webmaster@cprogramming.com

Popular pages
C tutorial
C Tutorial
Get started
Exactly how to get started with C++ (or C) today
Learn to program
5 ways you can learn to program faster
C++ tutorial
C++ Tutorial
Problems new programmers face
The 5 Most Common Problems New Programmers Face
Learn to make a game
How to make a game in 48 hours
Common problems
8 Common Programming Mistakes
C++11
What is C++11?
Image credits

You might also like