Let us see at below diagram, which helps us in explaining the diamond problem.
In the above picture, we have 2 classes B and C that derive from class – A. We also have class D that derives from both B and C by using multiple inheritance.
The compiler will get confused that from which base it needs to take.
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 41 42 43 44 45 46 47 48 49 50 |
using namespace std; /*This is my base class */ class MyBaseClass { public: MyBaseClass(); virtual ~MyBaseClass(); void MyFun (); }; MyBaseClass::MyBaseClass () { cout << "Constructor of MyBaseClass" << endl; } void MyBaseClass::MyFun () { cout << "I am in the Function" << endl; } MyBaseClass::~MyBaseClass () { cout << "Destructor of MyBaseClass" << endl; } /*This is my derived class which is derived from the base*/ class MyDerivedClass: public MyBaseClass { //Doing a lot of jobs by extending the functionality public: MyDerivedClass() { cout << "Constructor of MyDerivedClass" << endl; } ~MyDerivedClass() { cout << "Destructor of MyDerivedClass" << endl; } }; class MyDerived2Class: public MyBaseClass { //Doing a lot of jobs by extending the functionality public: MyDerived2Class() { cout << "Constructor of MyDerived2Class" << endl; } ~MyDerived2Class() { cout << "Destructor of MyDerived2Class" << endl; } }; class MyDerived3Class: public MyDerivedClass, public MyDerived2Class { //Doing a lot of jobs by extending the functionality public: MyDerived3Class () { cout << "Constructor of MyDerived3Class" << endl; } ~MyDerived3Class () { cout << "Destructor of MyDerived3Class" << endl; } }; int _tmain (int argc, _TCHAR* argv[]) { MyDerived3Class ocClas; ocClas.MyFun (); getchar (); return 0; } |
In the code above, we’ve given a more concrete example of the diamond problem. The MyBaseClass class corresponds to the topmost class in the hierarchy (A in our graphic above), MyDerivedClass and MyDerived2Class respectively correspond to B and C in the graphic, and the MyDerived3Class class corresponds to D.
So, let us look at the output of the above program
1 2 3 4 5 |
1>TestCPP.cpp(100): error C2385: ambiguous access of 'MyFun' 1> could be the 'MyFun' in base 'MyBaseClass' 1> or could be the 'MyFun' in base 'MyBaseClass' 1>TestCPP.cpp(100): error C3861: 'MyFun': identifier not found ========== Build: 0 succeeded, 1 failed, 0 up-to-date, 0 skipped ========== |
In our inheritance hierarchy, we can see that both the MyDerivedClass and MyDerived2Class classes derive from the MyBaseClass base class.
And here is the problem: because MyDerived3Class derives from both the MyDerivedClass and MyDerived2Class classes, which each have their own copy of the data members and methods of the MyBaseClass class.
At the end, the MyDerived3Class class object ocClas will contain two object of the MyBaseClass base class.
The compiler gets confused about which copy of the base object is to be taken, so the compiler throws an error that reports as shown above
What is the solution to the Diamond Problem?
Below is the solution for the diamond problem.
If the inheritance from the MyBaseClass class to both the MyDerivedClass and MyDerived2Class class is marked as virtual, then C++ will ensure that only one subobject of the MyBaseClass class will be created for every MyDerived3Class object.
Let us have a look at the example. Copy and compile it.
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 41 42 43 44 45 46 47 48 49 50 |
using namespace std; /*This is my base class */ class MyBaseClass { public: MyBaseClass(); virtual ~MyBaseClass(); void MyFun (); }; MyBaseClass::MyBaseClass () { cout << "Constructor of MyBaseClass" << endl; } void MyBaseClass::MyFun () { cout << "I am in the Function" << endl; } MyBaseClass::~MyBaseClass () { cout << "Destructor of MyBaseClass" << endl; } /*This is my derived class which is derived from the base*/ class MyDerivedClass: virtual public MyBaseClass { //Doing a lot of jobs by extending the functionality public: MyDerivedClass() { cout << "Constructor of MyDerivedClass" << endl; } ~MyDerivedClass() { cout << "Destructor of MyDerivedClass" << endl; } }; class MyDerived2Class: virtual public MyBaseClass { //Doing a lot of jobs by extending the functionality public: MyDerived2Class() { cout << "Constructor of MyDerived2Class" << endl; } ~MyDerived2Class() { cout << "Destructor of MyDerived2Class" << endl; } }; class MyDerived3Class: public MyDerivedClass, public MyDerived2Class { //Doing a lot of jobs by extending the functionality public: MyDerived3Class () { cout << "Constructor of MyDerived3Class" << endl; } ~MyDerived3Class () { cout << "Destructor of MyDerived3Class" << endl; } }; int _tmain (int argc, _TCHAR* argv[]) { MyDerived3Class ocClas; ocClas.MyFun (); getchar (); return 0; } |
In the above program, we have derived the base class using the “virtual” keyword to the MyDerivedClass and MyDerived2Class class declarations. Now the MyDerived3Class class object will have only one MyBaseClass sub-object, and the code below will compile just fine, the output of the above program is
1 2 3 4 5 |
Constructor of MyBaseClass Constructor of MyDerivedClass Constructor of MyDerived2Class Constructor of MyDerived3Class I am in the Function |