Tek-Tips is the largest IT community on the Internet today!

Members share and learn making Tek-Tips Forums the best source of peer-reviewed technical information on the Internet!

  • Congratulations Chriss Miller on being selected by the Tek-Tips community for having the most helpful posts in the forums last week. Way to Go!

Multiple inheritance 1

Status
Not open for further replies.

globos

Programmer
Nov 8, 2000
260
FR
Consider the following basic code:
Code:
class A
{
public:
  virtual void proc () = 0;//deferred procedure
};

class B : public A
{ };

class C : public A
{
public:
  virtual void proc ()//basic implementation
  { }
};

class D : public B, public C
{ };


int main ()
{
  D d;
  return 0;
}

As you see, D inherits 2 versions of proc a deferred one(from A) and an effective(from C). It should be logical that D inherits proc from C, as it is made effective. Instead of that the compiler badly complains of :
Code:
lign(22): error C2259: 'D' : cannot instantiate abstract class due to following members:
        lign(17) : see declaration of 'D'
lign(22): 'void __thiscall A::proc(void)' : pure virtual function was not defined
        lign(4) : see declaration of 'proc'
lign(22): 'D' : cannot instantiate abstract class due to following members:
        lign(17) : see declaration of 'D'
lign(22): 'void __thiscall A::proc(void)' : pure virtual function was not defined
        lign(4) : see declaration of 'proc'

The compiler(at least cl) can't see that C::proc is the version to use in class D.
The problem can be solved by redeclaring proc in class D :
Code:
class D : public C, public D
{
public:
  virtual void proc ()
  {
    C::proc ();
  }
};

In complex class hierarchies(where multiple inheritance is used), with many routines, this can lead to lots of silly redeclarations.

Does anyone know a better solution? I heard of the virtual operator for managing class multiple inheritance, but found nothing clear about it.

--
Globos
 
Well if you do this you get one copy of the base class but you will still need to disambiguate the call somehow I think.Stroustrup deals with this fully in his C++ Programming Language book.

class A
{
public:
virtual void proc () = 0;//deferred procedure
};

class B : virtual public A
{ };

class C : virtual public A
{
public:
virtual void proc ()//basic implementation
{ }
};

class D : public B, public C
{ };


int main ()
{
D d;
return 0;
}

The only problem with this is that proc is pure in B so proc is also pure in D so I dont think a D can be instantiated. If B provides an impl for proc() then it will no longer be pure in D and D will be instantiatable.
This is pure speculation based on what ive read. I have not tried this myself yet and do not understand dominance yet so cant give a full answer.
 
Here's what I tried from what you said:
Code:
#include <iostream>
using std::cout;
using std::endl;

class A
{
public:
  virtual void proc () = 0;
};

class B : virtual public A
{
};

class C : virtual public A
{
public:
  virtual void proc ()
  {
    cout << &quot;C::proc&quot; << endl;
  }
};

class D : public B, public C
{ 
};

int main ()
{
  D d;

  d.proc ();
  return 0;
}

No errors when compiling.
On execution,
Code:
C::proc
is printed. So every thing is ok!

--
Globos
 
Didnt know that would work like that. Thats very interesting. It looks like you could use that to specify interfaces in B and implementations in C and use the multiple inheritance to glue interface and implementation together in D. This looks similar to something I was reading about mixins. Maybe if you search for a mixin tutorial it would explain this better but it has something to do with dominance and the fact that C::proc() dominates B::proc(). As I said earlier I still haven't really got into multiple inheritance, virtual bases and dominance but there is plenty about this sort of thing in Stoustrup.
 
I can't see any glue interface and implementation together in the snippet above. True interface for all players was declared in abstract class A - that's all, it's a C++ classic.
1st snippet inheritance lattice (tree) looks like:
Code:
A{proc = 0} A{proc}
 \         /
  B - D - C{proc}
Class D inherits virtual proc member, but ALL paths from D to ALL distinct A(s) in the lattice must be resolved (see C++ Standard). The left path A::B::D leaves D as abstract - we can't build such objects.

Now let's draw 2nd snippet lattice:
Code:
  A{proc=0}
 / B   C{proc}
 \ /
  D
Now we have the only ONE A 'sub-object' with the the unique virtual C::proc. Path A::C::D 'resolves' any ambiguity, but it's a rather doubtful 'resolution' (see compiler warning about domination). Class D inherits (implemented) C::proc because of no other interrogated inheritances via all paths to the unique A in the lattice.

We see common C++ member lookup rules. No any new semantics or interface/implementation features...
 
Status
Not open for further replies.

Part and Inventory Search

Sponsor

Back
Top