If we don't care about performance, seems virtual inheritance can replace normal inheritance at any time. Is there any real situation that we can only use normal inheritance? In my opinion, when we use inheritance, we always want the derived class can convert to base class. But with normal inheritance, we still make the derived class's data is composited by base class's data. Even though such composition makes we can convert the derived class to base class, but that's not really requirement.
CodePudding user response:
Aside from performance issues (which will be paid on every virtual
function call through a virtual
base class), there are two major problems with making everything a virtual
base class.
First, each derived class must initialize all of the virtual base classes that it uses. All of them, all the way up the hierarchy. This is a huge hassle; every time you write a new derived class or even just a new constructor for the derived class, you now may have to forward a bunch of parameters manually. Worse, if a type in the middle of a hierarchy needs a new base class, every derived class has to know about it and possibly call particular constructors of it.
By contrast, with non-virtual
inheritance, each class in the hierarchy is responsible for initializing its bases without making derived classes even know those base classes exist. This makes non-virtual
inheritance a lot easier to use and a lot more composition-friendly. And maintainable.
Second, if you do this all the time, you can get cases where diamond inheritance is not what you wanted. Just because two classes happen to inherit from the same base class does not mean that a class derived from both wants to combine that base class into a single instance. Indeed, one of the reasons why virtual
inheritance is not the default is because such circumstances are actually quite rare.
CodePudding user response:
I know what to do now. Think about a classic diamond inheritance example.
B inherited from A. C inherited from A. D inherited from B and C.
A is composed by impA. B is composed by impA and something extension in B, I call it as impB. C is composed by impA and something extension in C, I call it as impC.
D should be composed by impA, impB, impC and impD. But with normal inheritance, D is composed by twice impA, once impB, once impC and impD. impA appeared twice and that's the diamond problem. To solve this, we can use virtual inheritance.
For virtual inheritance is counterintuitive (just like people in this question), there is a better way to solve such problem. We can just write impA, impB, impC and impD. Then make A inherited from impA. B inherited from impA and impB. C inherited from impA and impC. D inherited from impA, impB, impC and impD. Then problem is solved.
struct impA {
void a() {}
};
template <typename derived>
struct impB {
void b() {
auto&& d = static_cast<derived&>(*this);
d.a();
d.a();
}
};
template <typename derived>
struct impC {
void c() {
auto&& d = static_cast<derived&>(*this);
d.a();
d.a();
}
};
template <typename derived>
struct impD {
void d() {
auto&& d = static_cast<derived&>(*this);
d.a();
d.b();
d.c();
}
};
struct A : public impA {};
struct B : public impA, impB<B> {};
struct C : public impA, impC<C> {};
struct D : public impA, impB<D>, impC<D>, impD<D> {};
int main() {
A{}.a();
B{}.a();
B{}.b();
C{}.a();
C{}.c();
D{}.a();
D{}.b();
D{}.c();
D{}.d();
}