0

In my C++ I have something like the following

class AbstractA {
    void Foo() = 0;
    void Bar() = 0;
    void Woo() = 0;
};

class AbstractB : public AbstractA {
    void Doh() = 0;
};

class Obj1 : public AbstractA {
    void Foo() {}
    void Bar() {}
    void Woo() {}
};

Now I want to define a new class Obj2 that is Obj1 and is (implements) AbstractB. Basically

class Obj2 : public Obj1, public AbstractB {
    void Doh();
};

Here compilation error occurs.

After some considerations I suspect I must (re)define Foo(), Bar() and Woo() inside Obj2 because the compiler doesn't know the correct path to follow to resolve them (basically passing from Obj1 or AbstractB?). Am I correct about this point?

If so, and since my correct path to resolve Foo(), Bar() and Woo() is always passing from ObjA, is there any syntax to avoid parent class call for each method? In other words can I be more concise than

class Obj2 : public Obj1, public AbstractB {
    void Doh();
    void Foo() { A::Foo() }
    void Bar() { A::Bar() }
    void Woo() { A::Woo() }
}
curiousguy
  • 8,038
  • 2
  • 40
  • 58
c.bear
  • 1,197
  • 8
  • 21
  • 1
    Basic Principle if you want ot do multiple Inheritance you have to use Virtaul explicilty other wise compiler will prompt error because Compiler can`t decide the route of parent Multiple inheritance is also known as Diamond Problem – Usman Kurd Nov 26 '15 at 08:58

3 Answers3

2

You can use virtual

class Obj2 : public Obj1, public virtual AbstractB {
    void Doh();
};
Jerome
  • 529
  • 4
  • 14
2

You are trying to solve what has now become infamous in the C++ community as the dreaded "Diamond of Death Problem". Bjarne Stroustroup, the creator of C++, gives a classic illustration of this problem.

class Base {
  public:
  virtual void Method1() = 0;
  virtual void Method2() = 0;
};

class Base1 : public Base
{
  public:
  virtual void Method1();
  virtual void Method2();
};

class Base2 : public Base
{
  public:
    virtual void Method1();
    virtual void Method2();
}

class Concrete : public Base1, public Base2
{
  virtual void Method1();
  virtual void Method2();
}

Look at the above class hierarchy. As you rightly guessed, the compiler does not know what version of Method1() and Method2() should be taken into the definition of the class Concrete. Since both Base1 and Base2 have their own versions of Method1() and Method2(), the compiler has 2 options to pick these definitions and it just gets confused and starts throwing all those errors with the word "ambiguous" thrown all around the place.

The solution proposed by Bjarne Stroustroup for the problem is a subterfuge called "Virtual Inheritence". In effect, what you have to do is introduce the keyword virtual when you derive from the class "Base".

class Base1 : virtual public Base
{
  public:
  virtual void Method1();
  virtual void Method2();
};

class Base2 : virtual public Base
{
  public:
    virtual void Method1();
    virtual void Method2();
}

This tells the compiler that, though there are multiple copies of Base in Base1 and Base2, they should in effect point to the same version of Base. This ensures that when you define: class Concrete : public Base1, public Base2 { }

"Concrete" gets only a single copy of "Base", which both "Base1" and "Base2" are pointing to, thus resolving the ambiguity for the compiler. How does the compiler achieve this? Every class with virtual methods is associated with something called as virtual function table or vtable. The vtable has a pointer to all the virtual methods in that class. When the classes Base1 and Base2 are loaded, the vtables for these 2 classes hold a single pointer to the Base class. Similarly, when Concrete is loaded, the vtable for Concrete too points to the same single instance of Base, effectively ensuring that there is a single instance of Base.

There are points to note here when instantiating Concrete. You will have to explicitly call the constructor of Base, just like you would explicitly call the constructors of Base1 and Base2. Something like

Concrete() : Base(), Base1(), Base2()
{
}

Also, if there is an explicit call to the constructor of Base() in the constructors of Base1 and Base2, this would be skipped while instantiating Concrete and the constructor of Base will be invoked directly.

Faisal
  • 361
  • 1
  • 6
1

When using = 0 to create pure virtual methods you also need to use the virtual keyword e.g.

class AbstractA
{
   virtual void Foo() = 0;
   virtual void Bar() = 0;
   virtual void Woo() = 0;
};

In addition, you probably want to make these functions public. Another very important thing is to always declare a virtual destructor in classes designed to be used in inheritance, see for example: When to use virtual destructors?.

The class Obj1 implements the interface AbstractA and is therefore a concrete class. AbstractB extends the interface AbstractA with the method Doh. However this causes problems when you inherit Obj1 (which inherits AbstractA) and AbstractB (which also inherits AbstractA). You thus inherit AbstractA 'twice', which does not work unless you use virtual inheritance, see: https://en.wikipedia.org/wiki/Virtual_inheritance.

An easier approach is to not let AbstractB inherit from AbstractA.

A good way to declare your classes is therefore:

class AbstractA
{
public:
    virtual ~AbstractA() {}
    virtual void Foo() = 0;
    virtual void Bar() = 0;
    virtual void Woo() = 0;
};

class AbstractB
{
public:
    virtual ~AbstractB() {}
    virtual void Doh() = 0;
};

/* This is now a concrete class that implements the 'interface' AbstractA. */
class Obj1 : public AbstractA
{
public:
    void Foo() {} /* Implements AbstractA::Foo() */
    void Bar() {} /* Implements AbstractA::Bar() */
    void Woo() {} /* Implements AbstractA::Woo() */
};

/* Inherits Obj1's implementations of AbstractA and implements AbstractB */
class Obj2 : public Obj1, public AbstractB
{
public:
    void Doh() {} /* Implements AbstractB::Woo() */
};
Community
  • 1
  • 1
JonatanE
  • 941
  • 7
  • 19