Why does virtual inheritance need to be specified in the middle of a diamond hierarchy?

前端 未结 5 2331
醉梦人生
醉梦人生 2021-02-20 06:24

I have diamond hierarchy of classes:

    A
  /   \\
 B     C
  \\   /
    D

To avoid two copies of A in D, we need to use virtual inheritance a

相关标签:
5条回答
  • 2021-02-20 06:42

    Question: Why does virtual inheritance needs to be performed at B and C, even though the ambiguity is at D?

    Because B's and C's methods must know they might have to work on objects whose layout is much different from B's and C's own layouts. With single inheritance it is not a problem, because derived classes just append their attributes after parent's original layout.

    With multiple inheritance you cannot to that because there's no single parent's layout in the first place. Moreover (if you want to avoid A's duplication) parents' layouts need to overlap on A's attributes. Multiple inheritance in C++ hides quite a lot of complexity.

    0 讨论(0)
  • 2021-02-20 06:45

    Why does virtual inheritance needs to be performed at B and C, even though the ambiguity is at D? It would have been more intuitive if it is at D.

    In your example, B and C are using virtual specifically to ask the compiler to ensure there's only one copy of A involved. If they didn't do this, they're effectively saying "I need my own A base class, I'm not expecting to share it with any other derived object". This could be crucial.

    Example of not wanting to share a virtual base class

    If A was some kind of container, B was derived from it and stored some particular type of object - say "Bat", while C stores "Cat". If D expects to have B and C independently providing information on a population of Bats and Cats they'd be very surprised if a C operation did something to/with the Bats, or a B operation did something to/with the Cats.

    Example of wanting to share a virtual base class

    Say D needs to provide access to some functions or data members that are in A, say "A::x"... if A is inherited independently (non-virtually) by B and C, then the compiler can't resolve D::x to B::x or C::x without the programmer having to explicitly disambiguate it. This means D can't be used as an A despite having not one but two "is-a" relationships implied by the derivation chain (i.e. if B "is a" A, and D "is a" B, then the user may expect/need to use D as if D "is a" A).

    Why is this feature designed like this by standards committee?

    virtual inheritance exists because it's sometimes useful. It's specified by B and C, rather than D, because it's an intrusive concept in terms of the design of B and C, and also has implications for the encapsulation, memory layout, construction and destruction and function dispatch of B and C.

    What can we do if B and C classes are coming from 3rd party library ?

    If D needs to inherit from both and provide access to an A, but B and C weren't designed to use virtual inheritance and can't be changed, then D must take responsibility for forwarding any requests matching the A API to either B and/or C and/or optionally another A it directly inherits from (if it needs a visible "is A" relationship). That might be practical if the calling code knows it's dealing with a D (even if via templating), but operations on the object via pointers to the base classes will not know about the management D is attempting to perform, and the whole thing may be very tricky to get right. But it's a bit like saying "what if I need a vector and I've only got a list", "a saw and not a screwdriver"... well, make the most of it or get what you really need.

    EDIT: My answer was to indicate B and C classes that they should not invoke A's constructor whenever its derived object gets created, as it will be invoked by D.

    That's an important aspect of this, yes.

    0 讨论(0)
  • 2021-02-20 06:50

    I'm not sure of the exact reason they chose to design virtual inheritance this way, but I believe the reason has to do with object layout.

    Suppose that C++ was designed in a way where to resolve the diamond problem, you would virtually inherit B and C in D rather than virtually inheriting A in B and C. Now, what would the object layout for B and C be? Well, if no one ever tries to virtually inherit from them, then they'd each have their own copy of A and could use the standard, optimized layout where B and C each have an A at their base. However, if someone does virtually inherit from either B or C, then the object layout would have to be different because the two would have to share their copy of A.

    The problem with this is that when the compiler first sees B and C, it can't know if anyone is going to be inheriting from them. Consequently, the compiler would have to fall back on the slower version of inheritance used in virtual inheritance rather than the more optimized version of inheritance that is turned on by default. This violates the C++ principle of "don't pay what you don't use for," (the zero-overhead principle) where you only pay for language features you explicitly use.

    0 讨论(0)
  • 2021-02-20 06:51

    As A is the multiply-inherited class it is those that derive from it directly that have to do so virtual.

    If you have a situation where B and C both derive from A and you want both in D and you can't use the diamond, then D can derive from just one of B and C, and have an instance of the other, through which it can forward functions.

    workaround something like this:

    class B : public A; // not your class, cannot change
    class C : public A; // not your class, cannot change
    
    class D : public B; // your class, implement the functions of B
    class D2 : public C; // your class implement the functions of C
    
    class D
    {
       D2 d2;
    };
    
    0 讨论(0)
  • 2021-02-20 06:52

    In addition to templatetypedef answer, it may be pointed out that you also may wrap A into

    class AVirt:virtual public A{}; 
    

    and inherit other classes from it. You wil not need to mark explicitly other inheriances as virtual in this case

    0 讨论(0)
提交回复
热议问题