Virtual inheritance![]() Virtual inheritance is a C++ technique that ensures only one copy of a base class's member variables are inherited by grandchild derived classes. Without virtual inheritance, if two classes Instead, if classes This feature is most useful for multiple inheritance, as it makes the virtual base a common subobject for the deriving class and all classes that are derived from it. This can be used to avoid the diamond problem by clarifying ambiguity over which ancestor class to use, as from the perspective of the deriving class ( It is used when inheritance represents restriction of a set rather than composition of parts. In C++, a base class intended to be common throughout the hierarchy is denoted as virtual with the Consider the following class hierarchy. class Animal {
public:
virtual ~Animal() = default; // Explicitly show that the default class destructor will be made.
virtual void eat() {}
};
class Mammal: public Animal {
public:
virtual void breathe() {}
};
class WingedAnimal: public Animal {
public:
virtual void flap() {}
};
// A bat is a winged mammal
class Bat: public Mammal, public WingedAnimal {};
As declared above, a call to Bat bat;
Animal& animal = bat;
// error: which Animal subobject should a Bat cast into,
// a Mammal::Animal or a WingedAnimal::Animal?
To disambiguate, one would have to explicitly convert Bat bat;
Animal& mammal = static_cast<Mammal&>(bat);
Animal& winged = static_cast<WingedAnimal&>(bat);
In order to call In this case, the double inheritance of This situation is sometimes referred to as diamond inheritance (see Diamond problem) because the inheritance diagram is in the shape of a diamond. Virtual inheritance can help to solve this problem. The solutionWe can re-declare our classes as follows: class Animal {
public:
virtual ~Animal() = default;
virtual void eat() {}
};
// Two classes virtually inheriting Animal:
class Mammal: virtual public Animal {
public:
virtual void breathe() {}
};
class WingedAnimal: virtual public Animal {
public:
virtual void flap() {}
};
// A bat is still a winged mammal
class Bat: public Mammal, public WingedAnimal {};
The The ability to share a single instance of the Additional Example of Several AncestorsThis example to illustrates a case where base class A / \ B C \ / D | E Here, import std;
using std::string;
class A {
private:
string msg;
public:
explicit A(const string& s):
msg{x} {}
void test() {
std::println("Hello from A: {}", msg);
}
};
// B, C inherit A virtually
class B: virtual public A {
public:
B():
A("instance of B") {}
};
class C: virtual public A {
public:
C():
A("instance of C") {}
};
// since B, C inherit A virtually, A must be constructed in each child
// B() and C() constructors can be omitted
class D: public B, public C {
public:
D():
A("instance of D"), B(), C() {}
};
// D() constructor can be omitted
class E: public D {
public:
E():
A("instance of E"), D() {}
};
// breaks without constructing A:
// class D: public B, public C {
// public:
// D():
// B(), C() {}
// };
// breaks without constructing A
// class E: public D {
// public:
// E():
// D() {}
// };
int main(int argc, char* argv[]) {
D d;
d.test();
// prints: "hello from A: instance of D"
E e;
e.test();
// prints: "hello from A: instance of E"
}
Pure Virtual MethodsSuppose a pure virtual method is defined in the base class. If a deriving class inherits the base class virtually, then the pure virtual method does not need to be defined in that deriving class. However, if the deriving class does not inherit the base class virtually, then all virtual methods must be defined. import std;
using std::string;
class A {
protected:
string msg;
public:
explicit A(const string& s):
msg{s} {}
void test() {
std::println("Hello from A: {}", msg);
}
virtual void pureVirtualTest() = 0;
};
// since B, C inherit A virtually, the pure virtual method pureVirtualTest doesn't need to be defined
class B: virtual public A {
public:
explicit B([[maybe_unused]] const string& s = ""):
A("instance of B") {}
};
class C: virtual public A {
public:
explicit C([[maybe_unused]] const string& s = ""):
A("instance of C") {}
};
// since B, C inherit A virtually, A must be constructed in each child
// however, since D does not inherit B, C virtually, the pure virtual method in A *must be defined*
class D: public B, public C {
public:
explicit D([[maybe_unused]] const string& s = ""):
A("instance of D from constructor A"),
B("instance of D from constructor B"),
C("instance of D from constructor C") {}
void pureVirtualTest() override {
std::println("Pure virtual hello from: {}", msg);
}
};
// it is not necessary to redefine the pure virtual method after the parent defines it
class E: public D {
public:
explicit E([[maybe_unused]] const string& s = ""):
A("instance of E from constructor A"),
D("instance of E from constructor D") {}
};
int main(int argc, char* argv[]) {
D d("d");
d.test(); // Hello from A: instance of D from constructor A
d.pureVirtualTest(); // Pure virtual hello from: instance of D from constructor A
E e("e");
e.test(); // Hello from A: instance of E from constructor A
e.pureVirtualTest(); // Pure virtual hello from: instance of E from constructor A
}
References
|