when does ambiguity arise in multiple inheritance?
When you have replicated base class in several paths of inheritance and you are trying to cast to it or call its member-function.
struct A { };
struct B : A { };
struct C : A { };
struct D : B, C { }; // has replicated A as the base class
D d;
A* a = static_cast<A*>(&d); // oops
The problem has several remedies which depend on the context heavily (using virtual base classes, just refine the aforementioned cast, etc.)
More info here, especially here.
One famous example of ambiguity in multiple inheritance is the so-called Diamond Problem.
Summary:
"In object-oriented programming languages with multiple inheritance and knowledge organization, the diamond problem is an ambiguity that arises when two classes B and C inherit from A, and class D inherits from both B and C. If a method in D calls a method defined in A (and does not override the method), and B and C have overridden that method differently, then from which class does it inherit: B, or C?"
You can find details here: Wikipedia: Diamond Problem
struct Base{
void foo(){
}
};
struct Derived1 : public Base{
};
struct Derived2 : public Base{
};
struct Final : public Derived1, public Derived2{
};
int main(){
Final f;
f.foo();
}
See on Ideone. To fix, simply use virtual inheritance:
struct Derived1 : virtual public Base{
};
struct Derived2 : virtual public Base{
};
Another possibility for ambigouty is the following:
struct Base1{
void foo(){
}
};
struct Base2{
void foo(){
}
};
struct Final : public Base1, public Base2{
};
int main(){
Final f;
f.foo();
}
Again, on Ideone. To fix, simple make do the following in Final:
struct Final : public Base1, public Base2{
using Base1::foo;
// or
// using Base2::foo;
};
When it makes names used unclear
class baseX
{
private:
void* callA();//will never be ambiguous.
protected:
void* callB();
public:
void* callC();
}
class baseY
{
private:
void* callA();//will never be ambiguous.
protected:
void* callB();
public:
void* callC();
}
class derived: public baseX, public baseY
{
void someMethod()
{
void* x = baseX::callB();//not ambiguous
void* y = baseY::callB();//not ambiguous
void* z = callB();//ambiguose
}
}
void someFunction(derived& d)
{
void* x = d.CallC();//ambiguous
}
Ambiguity can also happen when the same class is the base through more than one route:
class Base
{
public void call();
}
class DerivedX : public Base
{
}
class DerivedY : public Base
{
}
class GrandChild : public DerivedX, public DerivedY //What does call() do?
{
}
This can be solved with virtual bases:
class Base
{
public void call();
}
class DerivedX : public virtual Base
{
}
class DerivedY : public virtual Base
{
}
class GrandChild : public DerivedX, public DerivedY //only one "Base" class in inheritance, shared between DerivedX and DerivedY
{
}
Related
I have a naive C++ inheritance class question. I have one base class Base, two derived classes DerivedA and DerivedB and two other distinct classes A and B which share the names of some methods.
Ideally I would like to design methods in the base class that use the methods of A and B. My attempt is the following
#include <cstdio>
class A{
public :
A(){};
void speak(){printf("hello A\n");};
};
class B{
public :
B(){};
void speak(){printf("hello B\n");};
};
class Base{
public:
Base(){};
void method(){property.speak();}
A property;
};
class DerivedA : public Base{
public:
using Base::Base;
A property;
};
class DerivedB : public Base{
public:
using Base::Base;
B property;
};
int main(int argc, char *argv[]){
DerivedA da;
DerivedB db;
da.property.speak();
db.property.speak();
// Attempting to call the shared method on the two distinct properties
da.method();
db.method();
}
Could you point me to a better design ? I suspect I should use templates but I am not sure how.
There are several options with different advantages and disadvantages.
You can make the base class a template like this:
template<class Property>
class Base{
public:
Base(){};
void method(){property.speak();}
Property property;
};
class DerivedA : public Base<A>{
};
class DerivedB : public Base<B>{
};
It is clear and simple, but notice that DerivedA and DerivedB in this case have no common base class, so you can't, e.g. put pointers to them in the same container.
You can use polymorphism.
class Base{
public:
virtual ~Base() {}
virtual void method() = 0;
};
class DerivedA : public Base{
public:
A property;
void method() override { property.speak(); }
};
class DerivedB : public Base{
public:
B property;
void method() override { property.speak(); }
};
This is a bit more complex, involves some code duplication and requires more resources (to store virtual table e.g.), but now the classes really have a common ancestor.
Combine templates and polymorphism
class Base{
public:
virtual ~Base() {}
virtual void method() = 0;
};
template<class Property>
class Derived: public Base {
public:
Property property;
void method() override { property.speak(); }
};
using DerivedA = Derived<A>;
using DerivedB = Derived<B>;
This is actually option #2, but with no code duplication: we make the compiler generate code for us using templates.
Other options. There may be other more specialized options depending on your requirements.
I have diamond problem when inheriting from templated base class which takes Derived class as template argument.
Normaly this problem is solvable with virtual inheritance like this:
class Base0
{
protected:
int var = 0;
};
class Base1 : public virtual Base0
{
};
class Base2 : public virtual Base0
{
};
class Derived :
public Base1,
public Base2
{
public:
void f()
{
var = 1; // OK single var
}
};
However I have this scenario:
template<typename DERIVED_CLASS>
class TemplatedBase0
{
protected:
int var = 0;
};
class Base1 : public virtual TemplatedBase0<Base1>
{
};
class Base2 : public virtual TemplatedBase0<Base2>
{
};
class Derived :
public Base1,
public Base2
{
public:
void f()
{
var = 1; // ERROR: var is ambigous
}
};
I understand that in above case templated base class is not the same, ie. there are 2 completely unrealted base classes involved and it looks like virtual inheritance dosnt work.
So my question is, is there any way to make this work? what design/approach should I take here?
I need to have base class as a template which takes derived class type. but how to make diamond inheritance possible?
If want to only have a single int var; in Derived, you need to move it to a non-template base class:
class GenericTemplatedBase0
{
protected:
int var = 0;
};
template<typename DERIVED_CLASS>
class TemplatedBase0 : virtual public GenericTemplatedBase0
{
// ...
};
class Base1 : public TemplatedBase0<Base1>
{
};
class Base2 : public TemplatedBase0<Base2>
{
};
class Derived : public Base1, public Base2
{
public:
void f()
{
var = 1;
}
};
There is no diamond in your code. Your Derived has two different vars. Depending on which you want you write:
Base1::var = 1;
Base2::var = 1;
but how to make diamond inheritance possible?
It is not clear why you want a diamond. If your aim is to have only a single var in Derived you need a different design.
I want to provide a stable API for a library I've written by providing a set of pure abstract classes.
Consider the following class hierarchy:
//Note: Destructors left out for readability
struct IBase{
virtual void something() = 0;
}
struct IDerivedA : public IBase{
virtual void another() = 0;
}
struct IDerivedB : public IBase{
virtual void another(int a) = 0;
}
There will be multiple implementations for these interfaces. To write an implementation, I would do:
struct Base : public IBase{
int x; //x is required to implement something()
virtual void something();
}
struct DerivedA : public IDerivedA, public Base{
virtual void another();
}
struct DerivedB : public IDerivedB, public Base{
virtual void another(int a);
}
This leads to the dreaded diamond problem, for which I see the following solutions:
Virtually inherit IBase in both IDerivedA and IDerivedB and just use multiple inheritance
Don't use Base at all and instead forward the calls to a helper class to avoid code duplication
Something like:
struct Base{
int x; //x is required to implement somethingImpl()
void somethingImpl();
}
struct DerivedA : public IDerivedA{
virtual void something(){ b.somethingImpl(); };
virtual void another();
private:
Base b;
}
struct DerivedB : public IDerivedB{
virtual void something(){ b.somethingImpl(); };
virtual void another(int a);
private:
Base b;
}
In case of 1), virtual inheritance would be required every time I use inheritance in the API layer.
In case of 2), I would need to wrap Base for every implementation class, which leads to code bloat.
I believe that there might be something fundamentally wrong in the design. Is there a general best practice for this specific case?
there's this code :
class Base{
public:
void disp(){
cout<<"base"<<endl;
}
};
class Der1:public Base{
public:
void test1(){
cout<<"der1 test1"<<endl;
}
};
class Der2:public Base{
public:
void test2(){
cout<<"der2 test2"<<endl;
}
};
class Der3:public Der1,Der2{
public:
void fun(){
cout<<"Der3 fun"<<endl;
}
};
int main()
{
Der3 d;
d.test1();
}
OUTPUT: der1 test1 //printed successfully
but for
int main()
{
Der3 d;
d.test2();
}
it gives error that Der2 is inaccessible ...
However when i change the code to
class Base{
public:
void disp(){
cout<<"base"<<endl;
}
};
class Der1:public Base{
public:
void test1(){
cout<<"der1 test1"<<endl;
}
};
class Der2:public Base{
public:
void test2(){
cout<<"der2 test2"<<endl;
}
};
class Der3:public Der2,Der1{ //***changed the order here***
public:
void fun(){
cout<<"Der3 fun"<<endl;
}
};
int main()
{
Der3 d;
d.test2();
}
it outputs: der2 Test2
Can someone explain what is happening here ?
It should be:
class Der3:public Der2, public Der1{
If you don't specify the access qualifier, it defaults to private.
Also because you have a common base in the two types inherited in Der3 you should use virtual inheritance in Der1 and Der2. This avoids replicating the common Base members (if any.)
class Der1:public virtual Base{...
class Der2:public virtual Base{...
You have to add the accessibility to each base class:
class Der3:public Der1, public Der2{
public:
void fun(){
cout<<"Der3 fun"<<endl;
}
};
When you switch the order Der2 is a public base but Der1 is private.
class Der3:public Der1,Der2 {
The 'public' there is only good for the next base class. You want to write:
class Der3: public Der1, public Der2 {
Also, it should be noted that this example also shows the deadly "diamond" inherience pattern, so it's designer clearly should be slapped.
By default, class inheritance is private. And so:
class Der3: public Der1, Der2
is the same as:
class Der3: public Der1, private Der2
You need to use public inheritance for both base classes:
class Der3: public Der1, public Der2
I am curious if there is a neat way to expose methods in the base class of a derived interface.
So in code: -
class cbase {
public:
void MyMethodA() { }
};
class cderived : public cbase {
public:
void MyMethodB() { }
}
class ibase {
public:
virtual void MyMethodA() = 0;
};
class iderived : public ibase {
public:
virtual void MyMethodB() = 0;
};
Now if I make cbase inherit ibase, and cderived implement iderived, the compiler will complain that when I instantiate cderived, MyMethodA() is abstract and not implemented.
MyMethodA() is implemented in the base class and through ibase. Is the only way to fix this to reimplement ibase's methods in the cderived class? If so, yuck!
So as below: -
class cbase : public ibase {
public:
void MyMethodA() { }
};
class cderived : public cbase, public iderived {
public:
void MyMethodA() { cbase::MyMethodA(); }
void MyMethodB() { }
};
cderived inst;
iderived *der = &inst;
der->MyMethodA();
der->MyMethodB();
ibase *bas = der;
bas->MyMethodA();
I hope this is enough to convey the question. :) It might sound a little loopy as we are trying to refactor old code.
I am sure there is plenty of eager commentary out there ;)
If I understand correctly you want to make ibase a virtual base class of iderived and cbase.
This was there is only one instance of each of your interface classes in the class hierarchy and when you join cbase and iderived together at the cderived level the non-virtual override of MyMethodA in cbase is used because it is the dominating override.
See here: Hidden Features of C++?
E.g.
class ibase {
public:
virtual void MyMethodA() = 0;
};
class iderived : public virtual ibase {
public:
virtual void MyMethodB() = 0;
};
class cbase : public virtual ibase {
public:
void MyMethodA() { }
};
class cderived : public cbase, public virtual iderived {
public:
void MyMethodB() { }
};
int main()
{
cderived inst;
iderived *der = &inst;
der->MyMethodA();
der->MyMethodB();
ibase *bas = der;
bas->MyMethodA();
}
Take a look into multiple inheritance.
The answer to that faq item is basically answer to your question :
class cderived : public virtual cbase {
public:
void MyMethodB() { }
}
class ibase {
public:
virtual void MyMethodA() = 0;
};
class iderived : public virtual ibase {
public:
virtual void MyMethodB() = 0;
};