Multiple Inheritance from Two Derived Classes

Multiple Inheritance from two derived classes

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.

Is multiple inheritance from the same base class via different parent classes really an issue here?

As said by Juraj Blaho the canonical way to solve this problem is virtual inheritance. One implementation model for virtual inheritance is to add an entry in the vtable of classes using virtual inheritance pointing to the real unique object from the base class. That way, you get the following inheritance graph :

             Model3D
/ \
Moveable3D Nameable
| |
Moveable |
\ /
Object

That is you must have :

class Nameable : public virtual Object{...};
class Moveable : public virtual Object{...};

The other classes do not need virtual inheritance


Without virtual inheritance the graph would have been

             Model3D
/ \
Moveable3D Nameable
| |
Moveable |
| |
Object Object

with 2 distinct instances of Object

The most tricky part in virtual inheritance is the call of constructors (ref Virtual Inheritance in C++, and solving the diamond problem) Because there is only a single instance of a virtual base class that is shared by multiple classes that inherit from it, the constructor for a virtual base class is not called by the class that inherits from it (which is how constructors are called, when each class has its own copy of its parent class) since that would mean the constructor would run multiple times. Instead, the constructor is called by the constructor of the concrete class. ... By the way, the constructors for virtual base classes are always called before the constructors for non-virtual base classes. This ensures that a class inheriting from a virtual base class can be sure the virtual base class is safe to use inside the inheriting class's constructor. The destructor order in a class hierarchy with a virtual base class follows the same rules as the rest of C++: the destructors run in the opposite order of the constructors. In other words, the virtual base class will be the last object destroyed, because it is the first object that is fully constructed.

But the real problem is that this Dreadful Diamond on Derivation is often considered as a bad hierarchy design and is explicitely forbidden in Java.

But IMHO, what C++ virtual inheritance does under the hood is not that far from what you would have if all your interface classes were pure abstract classes (only pure virtual public methods) not inheriting from Object and if all you implementation classes did inherit explicitely from Object. Using it or not is up to you : after all it is part of the language ...

Multiple Inheritance from two derived classes with templates and constructors

You need to pass a template-argument (in this case, T) to the base template-class.

Change this

template<typename T>
class NotAbsTotal : public AbsInit<T>, public AbsWork<T>
{
public:
T y;
NotAbsTotal(int x) : AbsInit<T>(x) // You need to pass the template parameter
{};
};

How to handle multiple inheritance when both inherited classes need a distinct member?

Yes, it is ambiguous and the compiler will complain.
You can write Major::moving or Minor::moving within the code of Arm's member functions to specify which you want.

I question whether you have a proper "isa" relationship here. An arm is not a motor. An arm has two motors. You should be using composition here instead of inheritance. Note that Major and Minor don't have any virtual functions so there is no reason to prefer inheritance.

Try:

class Arm {
Major major;
Minor minor;
void move() {
...
major.moving= false;
minor.moving= false;
...

It is more obvious now how to refer to the components. But with (multiple) inheritance it is the same idea. I think also you are introducing the names Major and Minor just to get around the limit of inheriting more than one copy. If you used composition, you can avoid that:

class Arm {
ServoPart major;
ServoPart minor;
...

Or, since they are the same type now, perhaps make an array instead. This makes it easy to "broadcast" the same operation to all of them:

class Arm {
ServoPart joints[2];
void move() {
...
for (auto j : joints) j.moving= false;

update

Your comment indicates ServoPart has a virtual doJob function. You should separate that into a pure interface that has no data members, and derive your single-motor class from that. Your composited class can also inherit from that interface.

struct Servo_Interface {
virtual void doJob() =0;
};

class Joint : Servo_Interface {
bool moving;
Servo servo;
public:
void doJob() override;
};

class Arm : Servo_Interface {
Joint joints[2];
public:
void doJob() override;
};

Note that the implementation of doJob in Arm can call doJob for each of the components it contains, but it is a separate function that does not have any automatic reliance on them. Both single Joint and Arm instances can be treated polymorphically since they have a common interface.

C++ multiple inheritance from base classes with members with same name

I'd put the common parts of mode1 and mode2 in a common base class, let's say Common, comprising then your data and member function operation2. Then, together with virtual inheritance, you can have two views on the same data, even at the same time if needed.

class common {
friend class mode1;
friend class mode2;
protected:
volatile uint8_t& reg1;
volatile uint8_t& reg2;
uint8_t data;

public:
virtual void operation2() final { // do something
};

};

class mode1 : public virtual common
{
public:
virtual void operation1() final { // do something
};
virtual void operation3() final { // do something }
};
};

class mode2 : public virtual common
{
public:
virtual void operation4() final { // do something
}
virtual void operation5() final { // do something
}
};

class mode1and2 : public mode1, public mode2
{
public:
void operation6() { // do something }
};
void operation7() { // do something }
};
};

Multiple inheritance with two interfaces, one which derives from first, and a derived class implementing the first interface

The mistake is in how virtual inheritance is being applied.

Specifically, this problem can be fixed by using virtual inheritence, just not in the way first described (i.e. not "public virtual Foo, public virtual PureVirtualBase").

Both Foo and PureVirtualDerivedBase need to virtually inherit from PureVirtualBase:

class PureVirtualBase {
public:
virtual ~PureVirtualBase() {}
virtual int IntFn() = 0;
};

class PureVirtualDerivedBase : public virtual PureVirtualBase {
public:
virtual ~PureVirtualDerivedBase() {}
virtual int OtherIntFn() = 0;
};

class Foo : public virtual PureVirtualBase {
public:
virtual int IntFn() { return intVal; }
protected:
int intVal;
};

class Bar : public Foo, public PureVirtualDerivedBase {
public
virtual int OtherIntFn() { return 123; }
};

In this way, Bar can add an implementation of OtherIntFn to Foo without losing Foo's implementation of IntFn.

Raviteja is correct that this is a variation of the diamond problem. However, we need Bar to inherit from PureVirtualDerivedBase to be able to add the additional functionality of OtherIntFn.

I agree with Revelnaut that it is good try to avoid this sort of thing, but there are some instances where doing so can be awkward / inconvenient, particularly when one interface is clearly a subset of another interface.

Here is a more tangible example:

class IList {
public:
virtual ~IList() {}
virtual int GetLength() = 0;
};

class IDynamicList : public virtual IList {
virtual ~IDynamicList() {}
virtual int GetCapacity() = 0;
};

class ListOfFoo : public virtual IList {
public:
virtual ~ListOfFoo() {}
virtual int GetLength() { return length; }
void UsefulFooFunction();

protected:
int length;
};

class DynamicFooList : public ListOfFoo, public IDynamicList {
public:
virtual ~DynamicFooList() {}
virtual int GetCapacity() { return capacity; }
void FooSpecificFnThatGrowsList();

protected:
int capacity;
};

In this case, IList's functionality is a subset of IDynamicList's functionality.

You could separate the interfaces, but then a pointer to IDynamicList won't be able to tell you the length of the list; it would only be able to tell you the capacity. In this case, it would be cumbersome to pass / retrieve an additional pointer to to get the functionality provided by IList when IDynamicList is clearly already a list.



Related Topics



Leave a reply



Submit