Inherit from multiple partial implementations of a

2019-03-18 06:25发布

Is it possible to have a number of partial implementations of an abstract interface, and then collect these partial implementations into a single concrete class by using multiple inheritence?

I have the following example code:

#include <iostream>

struct Base
{
    virtual void F1() = 0;
    virtual void F2() = 0;
};

struct D1 : Base
{
    void F1() override { std::cout << __func__ << std::endl; }
};

struct D2 : Base
{
    void F2() override { std::cout << __func__ << std::endl; }
};

// collection of the two partial implementations to form the concrete implementation
struct Deriv : D1, D2
{
    using D1::F1; // I added these using clauses when it first didn't compile - they don't help
    using D2::F2;
};

int main()
{
    Deriv d;
    return 0;
}

This fails to compile with the following errors:

main.cpp: In function ‘int main()’:
main.cpp:27:11: error: cannot declare variable ‘d’ to be of abstract type ‘Deriv’
main.cpp:19:8: note:   because the following virtual functions are pure within ‘Deriv’:
main.cpp:5:18: note:    virtual void Base::F1()
main.cpp:6:18: note:    virtual void Base::F2()

2条回答
女痞
2楼-- · 2019-03-18 06:33

Try inheriting virtually from Base:

struct D1 : virtual Base
{
    void F1() override { std::cout << __func__ << std::endl; }
};

struct D2 : virtual Base
{
    void F2() override { std::cout << __func__ << std::endl; }
};

Without the virtual inheritance, your multiple inheritance scenario looks like inheritance from two separate and incomplete base classes D1 and D2, neither of which can be instantiated.

查看更多
SAY GOODBYE
3楼-- · 2019-03-18 06:43

Is it possible to have a number of partial implementations of an abstract interface, and then collect these partial implementations into a single concrete class by using multiple inheritence?

Yes.

Each Base base class subobject brings two pure virtual functions. How many of those base subobjects do you want in Deriv?

  • If you want 2 Base base class subobject, Deriv::D1::Base and Deriv::D2::Base (so conversions from Deriv& to Base& would be ambiguous) then you will have 4 different virtual functions in Deriv: Deriv::D1::Base::F1(), Deriv::D1::Base::F2(), Deriv::D2::Base::F1(), Deriv::D2::Base::F2(). Only the first and last ones are implemented, so the two middle ones are pure virtual: Deriv::D1::Base::F2(), Deriv::D2::Base::F1(). You have two entirely independent inheritance relations: Deriv inherits from D1 and Deriv inherits from D2.
  • If you want only one Base base class subobject Deriv::Base, then you will have only 2 different virtual functions in Deriv: Base::F1(), Base::F2().

Non-virtual inheritance in C++ is "concrete" inheritance, like containment: struct D : B means that for each D object there is exactly one B base class subobject, just like struct C { M m; } means that for each C there is exactly one M class member subobject. These relations are one-to-one.

OTOH, virtual inheritance is a more "abstract": struct D : virtual B means that for each D object is associated with a B base class subobject, but this relation is many-to-one, like struct C { M &m; }.

In general, for any derived class D (here Deriv), and for any virtual base B of D (here Base), all the base classes of D virtually derived from B (here Deriv::D1, Deriv::D2) contribute to the overriding of the virtual functions in the base class:

  • Base::F1() is overridden by Deriv::D1::F1()
  • Base::F2() is overridden by Deriv::D2::F2()

Non-virtual and virtual inheritance are very different inheritance relations, just like if non-virtual member functions and a virtual function introduce different relations between functions with the same signature in derived and base classes (hiding vs. overriding).

Like virtual and non-virtual functions, virtual and non-virtual base classes must be used in different situations (and one is not "better" than the other in general).

How to "fix" your code without virtual inheritance?

struct Deriv : D1, D2
{
    using D1::F1; // I added these using clauses when it first didn't compile - they don't help
    using D2::F2;
};

Yes: using declaration control name-lookup, so they affect visibility and ambiguity issues, not virtual functions overriding.

With your original non-virtual inheritance based design, in order to make Deriv a concrete class, you would have to explicitly implement both F1() and F2() virtual function signatures (there are 4 virtual functions in Deriv, but with only 2 different signatures), so you need 2 function definitions:

struct Deriv : D1, D2
{
    void F1() override { D1::F1(); }
    void F2() override { D2::F2(); }
};

Note that Deriv::F1() overrides Deriv::D1::F1() and Deriv::D2::F1().

查看更多
登录 后发表回答