SyntaxStudy
Sign Up
C++ Multiple Inheritance and the Diamond Problem
C++ Beginner 1 min read

Multiple Inheritance and the Diamond Problem

C++ supports multiple inheritance, where a derived class can inherit from more than one base class. This is powerful but introduces complexity. The most notorious issue is the 'diamond problem': if class D inherits from both B and C, and both B and C inherit from A, then D would contain two copies of A's members without disambiguation. Virtual inheritance resolves this by making the compiler share a single base subobject. Virtual inheritance is declared by placing 'virtual' before the access specifier in the inheritance clause. The most-derived class in the hierarchy becomes responsible for initialising the virtual base, which changes constructor syntax and ordering. Because of this complexity, many modern C++ coding guidelines recommend composing functionality with interfaces (abstract classes with only pure virtual functions) rather than deep inheritance hierarchies. Access specifiers on inheritance control how the visibility of inherited members is adjusted in the derived class. Public inheritance preserves original access levels. Protected inheritance makes public and protected base members protected in the derived class. Private inheritance makes all base members private, effectively modelling implementation-in-terms-of rather than subtyping.
Example
#include <iostream>

// Common base
class Device {
public:
    virtual ~Device() = default;
    void powerOn() const { std::cout << "Device powered on\n"; }
};

// Two intermediate classes — use virtual inheritance
class Scanner : virtual public Device {
public:
    void scan() const { std::cout << "Scanning document\n"; }
};

class Printer : virtual public Device {
public:
    void print() const { std::cout << "Printing document\n"; }
};

// Diamond: inherits Scanner and Printer — only ONE Device subobject
class AllInOne : public Scanner, public Printer {
public:
    void doAll() const {
        powerOn();   // unambiguous — single Device subobject
        scan();
        print();
    }
};

// Interface (abstract class) — preferred over deep hierarchies
class ISerializable {
public:
    virtual std::string serialize() const = 0;
    virtual ~ISerializable() = default;
};

class Document : public ISerializable {
public:
    explicit Document(const std::string& text) : text_(text) {}
    std::string serialize() const override { return text_; }
private:
    std::string text_;
};

int main() {
    AllInOne aio;
    aio.doAll();

    ISerializable* doc = new Document("Hello, world");
    std::cout << doc->serialize() << "\n";
    delete doc;

    return 0;
}