I was wondering what was the best way to achieve code reuse by inheritance in a COM application.
The Problem
It came to my understanding that the virtual inheritance model of C++ cannot by used in a COM environment because it is not language independent.
In pure C++ a class hierarchy could look something like the following:
interface IBase {
virtual void BaseMethod() = 0;
};
interface IDerived : virtual IBase {
virtual void DerivedMethod() = 0;
};
class CBase : public virtual IBase {
public:
virtual void BaseMethod() override { /* Do something */ }
};
class CDerived : public IDerived, public CBase {
public:
virtual void DerivedMethod() override { /* Do something */ }
};
This results in the following hierarchy:
IDerived and CBase both inherit virtually from IBase.
Since virtual inheritance is not available in COM, the class hierarchy would rather look something like this:
interface IBase {
virtual void BaseMethod() = 0;
};
interface IDerived : IBase {
virtual void DerivedMethod() = 0;
};
class CBase : public IBase {
public:
virtual void BaseMethod() override { /* Do something */ }
};
class CDerived : public IDerived, public CBase {
public:
virtual void DerivedMethod() override { /* Do something */ }
};
This results in the following hierarchy:
At first glance, there seems to be a problem with the ambiguity of IBase. This can be easily resolved by implementing the IUnknown::QueryInterface method.
The real problem is, how can CDerived inherit the implemented methods of IBase from CBase, namely CBase::BaseMethod?
In the case of virtual inheritance, CBase::BaseMethod can be inherited by dominance, but without virtual inheritance this cannot happen: In CDerived, the method CBase::IBase::BaseMethod is defined, however the method IDerived::IBase::BaseMethod is not, resulting in the class CBase still being abstract and not thus not eligible for instantiation.
One way, to resolve this would be to override the method BaseMethod in CDerived once again:
class CDerived : public IDerived, public CBase {
public:
virtual void BaseMethod() override { CBase::BaseMethod(); }
virtual void DerivedMethod() override { /* Do something */ }
};
Does this have any performance implications? Is there a better way of achieving what I want? Doesn't the repeated override of the function in CDerived defeat the whole idea of inheritance?
ATL widely uses a technique that goes like this:
template <typename Itf>
class IBaseImpl : public Itf {
public:
void BaseMethod() override;
};
class CBase : public IBaseImpl<IBase> {};
class CDerived : public IBaseImpl<IDerived> {
public:
void DerivedMethod() override;
};
Now you have straight-line inheritance: IBase <- IDerived <- IBaseImpl<IDerived> <- CDerived.
Related
The following code only works if you uncomment the line
virtual void FuncA() { ImplA::FuncA(); }
in class ImplB, otherwise I get compiler error:
cannot instantiate abstract class ... FuncA(void)' : is abstract
Question is why doesn't it get the implement for FuncA() from the inherited ImplA?
class InterfaceA {
public:
virtual void FuncA()=0;
};
class InterfaceB : public InterfaceA {
public:
virtual void FuncB()=0;
};
class ImplA : public InterfaceA {
public:
virtual void FuncA() { printf("FuncA()\n"); }
};
class ImplB : public ImplA, public InterfaceB {
public:
// virtual void FuncA() { ImplA::FuncA(); }
virtual void FuncB() { printf("FuncB()\n"); }
};
{
ImplB *b = new ImplB();
InterfaceA *A= b;
A->FuncA();
InterfaceB *B= b;
B->FuncB();
B->FuncA();
}
You've hit an instance of the "diamond" problem in multiple inheritance.
You'll need to use "virtual" inheritance (which amounts to adding the keyword virtual when inheriting)
The problem is that ImplB has two paths to the base class InterfaceA. However, your intention is that the interfaces do not provide any implementation. Thus, you need to indicate this to the compiler, so it can unify the pure virtual functions.
For a much better explanation:
http://www.cprogramming.com/tutorial/virtual_inheritance.html
I've modified your code to add virtual when you inherit from interfaces. Now it compiles, even with the line commented. Also note, I think you are missing virtual destructors, so you'll have some other problems down the line. This code compiles, without uncommenting FuncA.
#include <cstdio>
class InterfaceA {
public:
virtual void FuncA()=0;
};
class InterfaceB : public virtual InterfaceA {
public:
virtual void FuncB()=0;
};
class ImplA : public virtual InterfaceA {
public:
virtual void FuncA() { printf("FuncA()\n"); }
};
class ImplB : public ImplA, public virtual InterfaceB {
public:
// virtual void FuncA() { ImplA::FuncA(); }
virtual void FuncB() { printf("FuncB()\n"); }
};
int main()
{
ImplB *b = new ImplB();
InterfaceA *A= b;
A->FuncA();
InterfaceB *B= b;
B->FuncB();
B->FuncA();
}
Multiple inheritance isn't "mixins"
You can inherit from multiple classes that have methods with the same name but that doesn't make them the same.
The thing that inherits from a virtual class must implement the pure virtual functions of its parents.
If the method names weren't scoped, you could end up with combinations of parent classes that were mutually exclusive to inherit from because the implementations of the method with the shared name wouldn't be compatible.
I am a bit surprised that putting using ImplA::FuncA; in ImplB doesn't solve it, though: https://gcc.godbolt.org/
Lets suppose you want to make an interface of the class Derived and it looks like this:
class Derived : public Base
{
public:
foo();
}
class Base
{
public:
tii();
//many other methods
}
How would you do the Interface? How can you make Base::tii visible (and also other methods) to this new interface?
class IDerived
{
public:
virtual foo() = 0;
// should I declare here tii() as a pure virtual function?
// but by doing it now there is ambiguity!
}
What is a good strategy?
The new Derived class should look like this....
class Derived : public Base, public IDerived
{
//implement the real thing
}
Your example is doing things backwards: the interface should be defined independently of any concrete classes with all pure virtual methods:
class IDerived
{
public:
virtual void foo() = 0;
virtual ~IDerived() {} // don't forget to include a virtual destructor
}
And the concrete classes will derive publicly from the interface:
class Derived : public Base, public IDerived
{
public:
void foo();
}
If you want IDerived to also declare methods that Derived inherits from Base, you can have Derived explicitly implement the method by calling the inherited implementation:
class Derived : public Base, public IDerived
{
public:
void foo();
void bar() { Base::bar(); }
}
At front, I dislike interfaces (they are grown by other languages than c++).
Anyway, if you have one, it should be complete: Hence have the 'tii() as a pure virtual function'. To resolve the conflict rewrite that function in 'Derived' (forward to Base::tii).
Must virtual methods be always implemented in derived class?
Can I write something like this?
<!-- language: lang-cpp -->
class BaseInterface
{
public:
virtual void fun_a() = 0;
virtual void fun_b() = 0;
virtual ~BaseInterface();
};
class Derived : public BaseInterface
{
void fun_a() { ... };
};
class FinalClass : public Derived
{
void fun_b() { ... };
}
int main()
{
FinalClass test_obj;
test_obj.fun_a(); // use Derived implementation or fail ???
test_obj.fun_b(); // use own implementation
BaseInterface* test_interface = new FinalClass();
test_interface->fun_a(); // fail or ok ???
test_interface->fun_b();
}
Is the code above correct?
Does another virtual method outflank exist?
Pure virtual methods always must be reimplemented in derived class?
Actually a derived class which is going to be instantiated.
In your code, you didn't make an object from Derived so it's OK.
Can i write something like this?
Yes.
You had some minor errors that I corrected them:
class BaseInterface
{
public:
virtual void fun_a() = 0;
virtual void fun_b() = 0;
virtual ~BaseInterface() {}; // You forget this
};
class Derived : public BaseInterface
{
public:
void fun_a() {} // This should be public as its base
};
class FinalClass : public Derived
{
public:
void fun_b() {} // This should be public as its base
};
int main()
{
FinalClass test_obj;
test_obj.fun_a();
test_obj.fun_b();
BaseInterface* test_interface = new FinalClass();
test_interface->fun_a();
test_interface->fun_b();
}
It makes Derived also an abstract class which you cannot instantiate, seeing you don't implement all the virtual functions from it's base, it becomes an abstract class you cannot directly instantiate.
See here: liveworkspace.org/code/6huYU$10
For the rest, your code should work.
Code is correct.
There is no special concept for interface in C++. All are classes. Some of the class methods can be pure virtual. It only means that compiler cannot create an instance of such class.
I want to implement the pure virtual methods from an interface using the implementation
provided by an concrete class without having to call explicitly the method from the concrete class. Example:
class InterfaceA{
public:
virtual void foo() = 0;
};
class InterfaceB:public InterfaceA{
public:
virtual void bar() = 0;
};
class ConcreteA : public InterfaceA{
public:
virtual void foo(){}//implements foo() from interface
};
class ConcreteAB: public InterfaceB, public ConcreteA{
public:
virtual void bar(){}//implements bar() from interface
};
In this scenario, the compiler asks for a implementation of foo() in class ConcreteAB, because InterfaceB does not have it implemented and it inherited from InterfaceA.
There is a way to tell the compiler to use the implementation from ConcreteA without using a wrapper calling ConcreteA::foo()?
Make InterfaceA a virtual base class.
class InterfaceB : public virtual InterfaceA {
public:
virtual void bar() = 0;
};
class ConcreteA : public virtual InterfaceA {
public:
virtual void foo(){}//implements foo() from interface
};
You need virtual inheritance.
Interface A, at the top of the hierarchy, should be inherited virtually by all immediate subclasses.
class InterfaceB:public virtual InterfaceA{
public:
virtual void bar() = 0;
};
class ConcreteA : public virtual InterfaceA{
public:
virtual void foo(){}//implements foo() from interface
};
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;
};