29

I have an abstract base class which acts as an interface.

I have two "sets" of derived classes, which implement half of the abstract class. ( one "set" defines the abstract virtual methods related to initialization, the other "set" defines those related to the actual "work". )

I then have derived classes which use multiple inheritance to construct fully defined classes ( and does not add anything itself ).

So: ( bad pseudocode )

class AbsBase {
  virtual void init() = 0;
  virtual void work() = 0;
}

class AbsInit : public AbsBase {
  void init() { do_this(); }
  // work() still abs
}

class AbsWork : public AbsBase {
  void work() { do_this(); }
  // init() still abs
}

class NotAbsTotal : public AbsInit, public AbsWork {
  // Nothing, both should be defined
}

First of all, can I do this? Can I inherit from two classes which are both derived from the same Base? (I hope so).

Here is the "real problem", though (I lied a bit above to simplify the example).

What I have really gone and done is add non abstract accessors methods to the base class:

class AbsBase {
public:
  void init() { init_impl(); }
  void work() { work_impl(); }

private:
  virtual void init_impl() = 0;
  virtual void work_impl() = 0;
}

Because, a common idiom is to make all virtual methods private.

Unfortunately, now both AbsInit, and AbsWork inherit these methods, and so NotAbsTotal inherits "two of each" ( I realize I may be butchering what is really happening at compile time ).

Anyway, g++ complains that: "request for member init() is ambiguous" when trying to use the class.

I assume that, had I used my AbsBase class as a pure interface, this would have been avoided ( assuming that the top example is valid ).

So: - Am I way off with my implementation? - Is this a limitation of the idiom of making virtual methods private? - How do I refactor my code to do what I want? ( Provide one common interface, but allow a way to swap out implementations for "sets" of member functions )

Edit:

Seems I am not the first one: http://en.wikipedia.org/wiki/Diamond_problem

Seems Virtual Inheritance is the solution here. I have heard of virtual inheritance before, but I have not wrapped my head around it. I am still open to suggestions.

mmocny
  • 8,775
  • 7
  • 40
  • 50

5 Answers5

38

It looks like you want to do virtual inheritance. Whether that turns out to actually be a good idea is another question, but here's how you do it:


class AbsBase {...};
class AbsInit: public virtual AbsBase {...};
class AbsWork: public virtual AbsBase {...};
class NotAbsTotal: public AbsInit, public AbsWork {...};

Basically, the default, non-virtual multiple inheritance will include a copy of each base class in the derived class, and includes all their methods. This is why you have two copies of AbsBase -- and the reason your method use is ambiguous is both sets of methods are loaded, so C++ has no way to know which copy to access!

Virtual inheritance condenses all references to a virtual base class into one datastructure. This should make the methods from the base class unambiguous again. However, note: if there is additional data in the two intermediate classes, there may be some small additional runtime overhead, to enable the code to find the shared virtual base class.

comingstorm
  • 25,557
  • 3
  • 43
  • 67
  • 1
    Why do you say that? What are the implications, other than syntax complexity, of virtual inheritance? – mmocny Oct 31 '08 at 19:51
  • Conversion of a base pointer to a derived class typically involves pointer adjustments, using a dynamic lookup table. This additional indirection introduces a cost of converting from base to derived. When calling a virtual method, this overhead is in the noise of the call. – Martin v. Löwis Oct 31 '08 at 23:32
1

It can be done, although it gives most the shivers.

You need to use "virtual inheritance", the syntax for which is something like

class AbsInit: public virtual AbsBase {...};
class AbsWork: public virtual AbsBase {...};
class NotAbsTotal: public AbsInit, public AbsWork {...};

Then you have to specify which function you want to use:

NotAbsTotal::work()
{
    AbsInit::work_impl();
}

(UPDATED with correct syntax)

James Curran
  • 101,701
  • 37
  • 181
  • 258
1

You need to to declare the inheritance as virtual:

struct AbsBase {
          virtual void init() = 0;
          virtual void work() = 0;
};

struct AbsInit : virtual public AbsBase {
          void init() {  }
};

struct AbsWork : virtual public AbsBase {
          void work() { }
};

struct NotAbsTotal : virtual public AbsInit, virtual public AbsWork {
};

void f(NotAbsTotal *p)
{
        p->init();
}

NotAbsTotal x;
Martin v. Löwis
  • 124,830
  • 17
  • 198
  • 235
  • This worked out well, even with my non-virtual public methods. However, I will have to try to see what happens when using a pointer of Base type to an object of Derived type, to make sure virtual inheritance works like virtual function overloading :) – mmocny Oct 31 '08 at 20:21
  • When converting from a virtual base to a derived class, you always need to use dynamic_cast<>, which in turn requires that a virtual method is defined in the virtual base. Plain usage of a virtual base should give no problems - it will polymorphically give access to the derived class. – Martin v. Löwis Oct 31 '08 at 23:29
0

I found a good and simple example at the below link. The article explains with an example program for calculating the area and perimeter of a rectangle. You can check it..cheers

Multilevel Inheritance is an inheritance hierarchy wherein one derived class inherits from multiple Base Classes. Read more..

http://www.mobihackman.in/2013/09/multiple-inheritance-example.html

Techman92
  • 1
  • 1
0

You have to start thinking in the terms of what you are trying to model here.

Public inheritance should only ever be used to model an "isa" relationship, e.g. a dog is a animal, a square is a shape, etc.

Have a look at Scott Meyer's book Effective C++ for an excellent essay on what the various aspects of OO design should only ever be interpreted as.

Edit: I forgot to say that while the answers so far provided are technically correct I don't think any of them address the issues of what you are trying to model and that is the crux of your problem!

HTH

cheers,

Rob

Rob Wells
  • 36,220
  • 13
  • 81
  • 146