Is multiple inheritance required?

I have a situation like below:

class A {
    virtual void f() { /*some default impl here*/}
    virtual void g() { /*some default impl here*/}
};

class B : public A {
    virtual void f() { /* do some specific stuff and call parent f()*/}
    virtual void g() { /* do some specific stuff and call parent g()*/}
};

class C : public A {
    virtual void f() { /* do some specific stuff and call parent f()*/}
    virtual void g() { /* do some specific stuff and call parent g()*/}
};

class mixed /* I don't know how to derive*/ {
    virtual void f() { /* call B::f()*/}
    virtual void g() { /* call C::g()*/}
};

      

I am thinking about multiple inheritance here. Ie, make mixed

derived from B

and C

. But there are some known issues (like Diamond Problem ).

Composition could be another solution.

But what is the correct solution, please advise :)

Thanks in advance!

+3


source to share


4 answers


some known issues (eg diamond problem).

First: there is no diamond template unless you explicitly created one.

 class mixed: public B, public C

      

This will result in mixed inheritance from B and C. Each has its own explicit A (no diamond).

Since both B and C have virtual members that are derived from A, it becomes ambiguous to be called, but you figured that by having explicit definitions of all virtual objects in mixed

(this is how the problem is solved).

  void mixed::f() { B::f();} // works fine.

      

Now, even if you are clearly creating a diamond.

Note. The diamond chart is not displayed normally. The diamond pattern is a design decision that needs to be made explicitly , and you use it to solve certain types of problems (using virtual inheritance).

class B: public virtual A ...
class C: public virtual A ...
class mixed: public B, public C ...

      



You still have no problem. Since mixed::f()

only uses branch B

(and then A). So far mixed::g()

only uses branch C

(and then A).

Even if it A

has its own state (although this is probably a bad idea, it is usually best to use interfaces as a virtual base class), then we don't have a problem because mixed::f()

and mixed::g()

only call a function in one child (problems start to arise if they call both hand, and the state A

becomes mutated on both calls.

Composition could be another solution.

This will work as well.

 class mixed: public A
 {
     B    b;
     C    c;
     public:
         virtual void f() override {b.f();}
         virtual void g() override {c.g();}
     ....
 };

      

But what's the right decision

There is no right solution. It will depend on the details you did not mention (for example, which details are A).

BUT the general advice is to prefer composition over inheritance, but these are only general guidelines that will always boil down to the actual problem.

+4


source


The fact that each method must "do stuff" before calling the parent causes a problem.

One solution would be to have A

, and B

as members of the class mixed

. Then you can control what you need to do with them in mixed::f()

andmixed::g()



If you need, you can create a base class base

with pure virtual functions f()

and g()

. mixed

can inherit from this, and therefore can A

, B

and C

. You are referring to this possibility when you discuss the composition.

+3


source


I think you can search for something like this. (Sorry, this is a big bunch of code, but this is really straight forward.)

#include <iostream>

struct A
{
  virtual void
  f()
  {
    std::cout << __PRETTY_FUNCTION__ << '\n';
  }

  virtual void
  g()
  {
    std::cout << __PRETTY_FUNCTION__ << '\n';
  }

  // Don't forget the virtual destructor.
  virtual ~A() noexcept = default;
};

struct B : virtual A
{
  virtual void
  f() override
  {
    std::cout << __PRETTY_FUNCTION__ << '\n';
    A::f();
  }

  virtual void
  g() override
  {
    std::cout << __PRETTY_FUNCTION__ << '\n';
    A::g();
  }
};

struct C : virtual A
{
  virtual void
  f() override
  {
    std::cout << __PRETTY_FUNCTION__ << '\n';
    A::f();
  }

  virtual void
  g() override
  {
    std::cout << __PRETTY_FUNCTION__ << '\n';
    A::g();
  }
};

struct D : virtual B, virtual C
{
  virtual void
  f() override
  {
    std::cout << __PRETTY_FUNCTION__ << '\n';
    B::f();
  }

  virtual void
  g() override
  {
    std::cout << __PRETTY_FUNCTION__ << '\n';
    C::g();
  }
};

int
main()
{
  D d {};
  d.f();
  std::cout << '\n';
  d.g();
}

      

override

is a C ++ 11 feature to check the compiler you are actually overriding. It is good practice to use it, but not required if your compiler does not support it. __PRETTY_FUNCTION__

is a GCC extension to get a string literal that names the current function signature. Standard C ++ has __func__

, but is less useful here. You can type strings yourself if your compiler doesn't have a function comparable to __PRETTY_FUNCTION__

.

Output:

virtual void D::f()
virtual void B::f()
virtual void A::f()

virtual void D::g()
virtual void C::g()
virtual void A::g()

      

It works, but I don't find this pretty code. Composition will probably be the best solution here.

+3


source


Here is an alternative to virtual inheritance: using CRTP to mix functions B

and C

in M

, using a generic one A

, without the overhead of vtables.

#include <iostream>

struct A
{
    int a;
};

template <typename T>
struct B
{
    A *get_base_a() {return static_cast<T*>(this);}
    void print_a() {std::cout << get_base_a()->a << '\n';}
};

template <typename T>
struct C
{
    A *get_base_a() {return static_cast<T*>(this);}
    void print_a() {std::cout << get_base_a()->a << '\n';}
};

struct M : A, B<M>, C<M>
{
};

int main()
{
    M m;
    m.a = 1;
    m.B::print_a();
    m.C::print_a();
    return 0;
}

      

Note that you cannot pass to a M*

function that expects B*

or C*

.

+1


source







All Articles