Diamond Problem in C++

The Diamond Inheritance Problem in C++ is something that can occur when performing multiple inheritance between Classes. Multiple Inheritance is the concept of inheriting multiple classes at once, instead of just one. If done incorrectly, it can result in the Diamond Problem.


What is the Diamond Inheritance Problem in C++?

The following image illustrates the situation under which the Diamond problem occurs.

Diamond Inheritance Problem in C++

The Class A, is inherited by both Class B and C. Then Class D performs multiple inheritance by inheriting both B and C. Now why does this cause a problem?

Well, let’s take a look at another diagram, that represents what actually ends up happening.

Diamond Problem C++

You may, or may not know, that when inheritance occurs between a parent and child class, an object of the parent is created and stored alongside the child object. That is how the functions and variables of the parent can be accessed.

Now if we apply this analogy to the above diagram, you will realize that the Object D, will end up having 4 more objects with it. Now the number of objects isn’t the problem, rather the problem is that we have two objects of Class A alongside Object D. This is a problem, because instead of one object, we have two objects of Class A. Other than performance reasons, what effect could this have? Let’s find out.


Problems caused by Diamond Inheritance

Let’s take a look at the actual code, and observe the problems being caused. As we mentioned earlier, due to Diamond Inheritance, the Object D will have two copies of Object A. Let’s run the below code to see proof of this fact.

#include <iostream>
using namespace std;

class A {
public:
  A()  { cout << "Class A constructor called" << endl; }
};

class B: public A {
public:
  B()  { cout << "Class B constructor called" << endl; }
};

class C: public A {
public:
  C()  { cout << "Class C constructor called" << endl; }
};

class D: public B, public C {
public:
  D()  { cout << "Class D constructor called" << endl; }
};

int main() {
    D d;
}
Class A constructor called
Class B constructor called
Class A constructor called
Class C constructor called
Class D constructor called

As you can see, the Constructor for Class A was called twice, meaning two objects were created.


Digging Deeper in the Diamond Problem

Let’s take a look at a more detailed example, that shows a situation under which Diamond Inheritance can actually become a real problem, and throw an error.

#include <iostream>
using namespace std;

class A {
    int var;
public:
    A(int n) {
        cout << "Class A constructor called" << endl;
        var = n;
    }

    A() { var = 10; }

    int getVar() { return var; }
};

class B: public A {
public:
    B(int n) : A(n) {
        cout << "Class B constructor called" << endl;
    }
};

class C: public A {
public:
    C(int n) : A(n) {
        cout << "Class C constructor called" << endl;
    }
};

class D: public B, public C {
public:
    D(int n1, int n2) : B(n1), C(n2) {
        cout << "Class D constructor called" << endl;
    }
};

int main() {
    D d = D(5, 8);
    cout << d.B::getVar() << endl;
    cout << d.C::getVar() << endl;
}

Take a good look at this code. What we are doing here, is using the parameterized constructors to initialize the Objects for Class A. Class B and C are both passed two values, 5 and 8, which they both use to initialize to objects for Class A. One of the objects has the value 5, and one of them has the value 8.

The reason we did this, is so we can highlight the fact that there are two objects with two different values. Now the question is, when we try to use the getVar() function on object D, what happens? Normally, if there were just one object of Class A that was initialized to value “n”, the value “n” would be returned. But what happens in this case?

int main() {
    D d = D(5, 8);
    cout << d.B::getVar() << endl;
    cout << d.C::getVar() << endl;
}
Class A constructor called
Class B constructor called
Class A constructor called
Class C constructor called
Class D constructor called
5
8

We can access each of the objects for Class A using the method above. Where we are performing the getVar() function on both the B and C Class object that are stored within Object D. As you can see, the value 5 and 8 are printed out.

But what happens when we use the getVar() function on D? Let’s find out.

int main() {
    D d = D(5, 8);
    cout << d.getVar() << endl;
}
error: request for member 'getVar' is ambiguous
note: candidates are: 'int A::getVar()'
note:                 'int A::getVar()'

See this error? It’s happening because it doesn’t know which object to use. It’s even showing in the error, that there are two objects which the getVar() function could be applied on.


How to solve the Diamond Problem in C++ using Virtual

The fix is very simple. All we need to do, is when inheriting when we make class B and class C inherit from Class A, we add the virtual keyword after the colon in the class name.

What this does, is shifts the responsibility of initializing Class A, to Class D. Previously Class B and C were responsible for initializing it, but since both were doing it, things became ambiguous. So if only Class D initializes Class A, there won’t be any problems as only one object is created.

Let’s modify our code now to include the Virtual keyword, and initialize Class A using Class D. (Although you can choose not to initialize it as well, and let the default constructor for Class A be used)

#include <iostream>
using namespace std;

class A {
    int var;
public:
    A(int n) {
        cout << "Class A constructor called" << endl;
        var = n;
    }

    A() { var = 10; }

    int getVar() { return var; }
};

class B: virtual public A {
public:
    B(int n) : A(n) {
        cout << "Class B constructor called" << endl;
    }
};

class C: virtual public A {
public:
    C(int n) : A(n) {
        cout << "Class C constructor called" << endl;
    }
};

class D: public B, public C {
public:
    D(int n1, int n2, int n3) : B(n1), C(n2), A(n3) {
        cout << "Class D constructor called" << endl;
    }
};

int main() {
    D d = D(5, 8, 10);
    cout << d.getVar() << endl;
}
Class A constructor called
Class B constructor called
Class C constructor called
Class D constructor called
10

See our output now? There is no error, and only one object for Class A has been created. With this, our job is done and we can continue programming in peace.


This marks the end of the Diamond Problem in C++ Tutorial. Any suggestions or contributions for CodersLegacy are more than welcome. Questions regarding the tutorial content can be asked in the comments section below.

Subscribe
Notify of
guest
0 Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments