In C++, what’s the diamond problem, and how can it be avoided?

 

Taking a look at the graphic below helps in explaining the diamond problem.

Suppose we have 2 classes B and C that derive from the same class – in our example above it would be class A. We also have class D that derives from both B and C by using multiple inheritance. You can see in the figure above that the classes essentially form the shape of a diamond – which is why this problem is called the diamond problem. Now, let’s take the graphic above and make it more concrete by translating it into actual code:

/*
The Animal class below corresponds to class 
A in our graphic above
*/
								
class Animal { /* ... */ }; // base class
{
int weight;

public:

int getWeight() { return weight;};

};

class Tiger : public Animal { /* ... */ };

class Lion : public Animal { /* ... */ }	
						
class Liger : public Tiger, public Lion { /* ... */ };	

Subscribe to our newsletter on the left to receive more free interview questions!

In the code above, we’ve given a more concrete example of the diamond problem. The Animal class corresponds to the topmost class in the hierarchy (A in our graphic above), Tiger and Lion respectively correspond to B and C in the graphic, and the Liger class corresponds to D.

Now, the question is what is the problem with having an inheritance hierarcy like this. Take a look at the code below so that we can best answer that question:

int main( )
{
Liger lg ;

/*COMPILE ERROR, the code below will not get past
any C++ compiler */

int weight = lg.getWeight();  
}

In our inheritance hierarchy, we can see that both the Tiger and Lion classes derive from the Animal base class. And here is the problem: because Liger derives from both the Tiger and Lion classes – which each have their own copy of the data members and methods of the Animal class- the Liger object "lg" will contain two subobjects of the Animal base class.

So, you ask, what’s the problem with a Liger object having 2 sub-objects of the Animal class? Take another look at the code above – the call "lg.getWeight()" will result in a compiler error. This is because the compiler does not know whether the call to getWeight refers to the copy of getWeight that the Liger object lg inherited through the Lion class or the copy that lg inherited through the Tiger class. So, the call to getWeight in the code above is ambiguous and will not get past the compiler.

Solution to the Diamond Problem

We’ve given an explanation of the diamond problem, but now we want to give you a solution to the diamond problem. If the inheritance from the Animal class to both the Lion class and the Tiger class is marked as virtual, then C++ will ensure that only one subobject of the Animal class will be created for every Liger object. This is what the code for that would look like:

class Tiger : virtual public Animal { /* ... */ };

class Lion : virtual public Animal { /* ... */ }

You can see that the only change we’ve made is to add the "virtual" keyword to the Tiger and Lion class declarations. Now the Liger class object will have only one Animal subobject, and the code below will compile just fine:

int main( )
{
Liger lg ;

/*THIS CODE WILL NOW COMPILE OK NOW THAT WE'VE
USED THE VIRTUAL KEYWORD IN THE TIGER AND LION
CLASS DECLARATIONS */

int weight = lg.getWeight();  
}


Hiring? Job Hunting? Post a JOB or your RESUME on our JOB BOARD >>

Subscribe to our newsletter for more free interview questions.



FOLLOW Varoon Sahgal, Author of ProgrammerInterviewon