views:

1008

answers:

4

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.

A: 

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
The syntax you use is incorrect: AbsInit and AbsWork should virtually derive from their base class, NotAbsTotal shouldn't.
Jasper Bekkers
That's why I said "something like" (it's been a long time since I had to do this,....)
James Curran
+1  A: 

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
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 :)
michalmocny
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
+8  A: 

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
Why do you say that? What are the implications, other than syntax complexity, of virtual inheritance?
michalmocny
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
A: 

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