Reputation: 71
I made a test code as following:
#include <iostream>
using namespace std;
#ifndef interface
#define interface struct
#endif
interface Base
{
virtual void funcBase() = 0;
};
interface Derived1 : public Base
{
virtual void funcDerived1() = 0;
};
interface Derived2 : public Base
{
virtual void funcDerived2() = 0;
};
interface DDerived : public Derived1, public Derived2
{
virtual void funcDDerived() = 0;
};
class Implementation : public DDerived
{
public:
void funcBase() { cout << "base" << endl; }
void funcDerived1() { cout << "derived1" << endl; }
void funcDerived2() { cout << "derived2" << endl; }
void funcDDerived() { cout << "dderived" << endl; }
};
int main()
{
DDerived *pObject = new Implementation;
pObject->funcBase();
return 0;
}
The reason I wrote this code is to test if the function funcBase() can be called in an instance of DDerived or not. My C++ complier (Visual Studio 2010) gave me a compile error message when I tried to compile this code. In my opinion, there is no problem in this code because it is certain that the function funcBase()
will be implemented (thus overriden) in some derived class of the interface DDerived
, because it is pure virtual. In other words, any pointer variable of type Implementation *
should be associated with an instance of a class deriving Implentation and overriding the function funcBase()
.
My question is, why the compiler give me such an error message? Why the C++ syntax is defined like that; i.e., to treat this case as an error? How can I make the code runs? I want to allow multiple inheritance of interfaces. Of course, if I use "virtual public" or re-declare the function funcBase()
in Implementation
like
interface DDerived : public Derived1, public Derived2
{
virtual void funcBase() = 0;
virtual void funcDDerived() = 0;
};
then everything runs with no problem.
But I don't want to do that and looking for more convenient method, because virtual inheritance may degrade the performance, and re-declaration is so tedious to do if inheritance relations of classes are very complex. Is there any methods to enable multiple inheritance of interfaces in C++ other than using virtual inheritance?
Upvotes: 7
Views: 7580
Reputation: 2368
I think C++ Standard 10.1.4 - 10.1.5 can help you to understand the problem in your code.
class L { public: int next; /∗ ... ∗/ };
class A : public L { /∗...∗/ };
class B : public L { /∗...∗/ };
class C : public A, public B { void f(); /∗ ... ∗/ };
10.1.4 A base class specifier that does not contain the keyword virtual, specifies a non-virtual base class. A base class specifier that contains the keyword virtual, specifies a virtual base class. For each distinct occurrence of a non-virtual base class in the class lattice of the most derived class, the most derived object (1.8) shall contain a corresponding distinct base class subobject of that type. For each distinct base class that is specified virtual, the most derived object shall contain a single base class subobject of that type. [ Example: for an object of class type C, each distinct occurrence of a (non-virtual) base class L in the class lattice of C corresponds one-to-one with a distinct L subobject within the object of type C. Given the class C defined above, an object of class C will have two subobjects of class L as shown below.
10.1.5 In such lattices, explicit qualification can be used to specify which subobject is meant. The body of function C::f could refer to the member next of each L subobject: void C::f() { A::next = B::next; } // well-formed. Without the A:: or B:: qualifiers, the definition of C::f above would be ill-formed because of ambiguity
So just add qualifiers when calling pObject->funcBase() or solve ambiguity in another way.
pObject->Derived1::funcBase();
Updated: Also very helpful reading will be 10.3 Virtual Functions of Standard.
Have a nice weekend :)
Upvotes: 1
Reputation: 490108
As you've defined it, your object structure looks like this:
The important point here is that each instance of Implementation
contains two entirely separate instances of Base
. You're providing an override of Base::funcBase
, but it doesn't know whether you're trying to override funcBase
for the Base
you inherited through Derived1
, or the Base
you inherited through Derived2
.
Yes, the clean way to deal with this is virtual inheritance. This will change your structure so there's only one instance of Base:
This is almost undoubtedly what you really want. Yes, it got a reputation for performance problems in the days of primitive compilers and 25 MHz 486's and such. With a modern compiler and processor, you're unlikely to encounter a problem.
Another possibility would be some sort of template-based alternative, but that tends to pervade the rest of your code -- i.e., instead of passing a Base *
, you write a template that will work with anything that provides functions A, B, and C, then pass (the equivalent of) Implementation
as a template parameter.
Upvotes: 7
Reputation: 2349
DDerived *pObject = new Implementation;
pObject->funcBase();
This creates a pointer of type DDerived to a Implementation. When you are using DDerived you really just have a pointer to an interface.
DDerived does not know about the implementation of funcBase because of the ambiguity of having funcBase being defined in both Derived1 and Derived2.
This has created a inheritance diamond which is what is really causing the problem.
http://en.wikipedia.org/wiki/Diamond_problem
I also had to check on the interface "keyword" you have in there
it's an ms-specific extension that's recognised by visual studio
Upvotes: 1
Reputation: 96241
The C++ language is designed in such a way that in your first approach without virtual inheritance there will be two parent copies of the method and it can't figure out which one to call.
Virtual inheritance is the C++ solution to inheriting the same function from multiple bases, so I would suggest just using that approach.
Alternately have you considered just not inheriting the same function from multiple bases? Do you really have a derived class that you need to be able to treat as Derived1
or Derived2
OR Base
depending on the context?
In this case elaborating on a concrete problem rather than a contrived example may help provide a better design.
Upvotes: 3