A class can inherit from several bases. That's safe for interfaces, but when two bases share a common ancestor you get the diamond problem — solved by virtual inheritance.
Multiple inheritance
#include <iostream>
struct Printable { virtual void print() const = 0; virtual ~Printable() = default; };
struct Serializable { virtual std::string serialize() const = 0; virtual ~Serializable() = default; };
struct Point : Printable, Serializable { // implements two interfaces
int x = 1, y = 2;
void print() const override { std::cout << "Point(" << x << ", " << y << ")\n"; }
std::string serialize() const override { return std::to_string(x) + "," + std::to_string(y); }
};
int main() {
Point p;
const Printable& pr = p;
const Serializable& se = p;
pr.print();
std::cout << se.serialize() << '\n';
}Point(1, 2)
1,2The diamond problem
Device → Scanner / Printer → Copier
#include <iostream>
struct Device { int id = 0; };
struct Scanner : Device {};
struct Printer : Device {};
struct Copier : Scanner, Printer {}; // contains TWO Device sub-objects
int main() {
Copier c;
c.Scanner::id = 1; // must disambiguate
c.Printer::id = 2;
std::cout << c.Scanner::id << ' ' << c.Printer::id << ' ' << sizeof(Copier) / sizeof(int) << '\n';
// c.id = 5; // error: ambiguous
}1 2 2Virtual inheritance
#include <iostream>
struct Device {
int id;
explicit Device(int i) : id(i) { std::cout << "Device(" << i << ")\n"; }
};
struct Scanner : virtual Device { Scanner() : Device(1) {} }; // shared base
struct Printer : virtual Device { Printer() : Device(2) {} };
struct Copier : Scanner, Printer {
Copier() : Device(3) {} // the MOST-DERIVED class initialises the virtual base
};
int main() {
Copier c;
std::cout << c.id << '\n'; // one Device, no ambiguity
}Device(3)
3With
virtualinheritance there is exactly one shared base sub-object.The most-derived class constructs the virtual base; the intermediate classes' initialisers for it are ignored.
Cost: an extra indirection (virtual base offset) to reach the shared base, and more complex object layout.
Name clashes between bases
#include <iostream>
struct Camera { void start() const { std::cout << "camera on\n"; } };
struct Phone { void start() const { std::cout << "phone on\n"; } };
struct SmartPhone : Camera, Phone {
void start() const { Phone::start(); Camera::start(); } // resolve explicitly
};
int main() { SmartPhone{}.start(); }phone on
camera onWhat is the diamond problem and how does C++ solve it?