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?
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.
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/
I am trying to figure out how to arrange some classes. This is what I've got so far ...
The top of the inheritance hierarchy is (naturally) T:
(T.h)
namespace foo
{
class T
{
public:
virtual void method1(std::string a_parameter) = 0;
virtual void method2() = 0;
};
}
I have two sub-classes of T with some additional methods - here are
the header files:
(A.h)
namespace foo
{
class A : public T
{
public:
virtual ~A() {};
virtual void method3() = 0;
//and a factory function
static A* gimmeAnAyy();
};
}
(B.h)
namespace foo
{
class B : public T
{
public:
virtual ~B() {};
virtual void method4() = 0;
//and a factory function
static B* gimmeABee();
};
}
The implementation classes are in the respective .cpp files:
(A.cpp)
namespace foo
{
class AImpl : public A
{
public:
A(std::string member_data) : m_member_data(member_data) {};
~A() {};
void method3()
{
//something really important, can't think of it right now ;-)
};
private:
std::string m_member_data;
};
A* A::gimmeAnAyy()
{
return new AImpl("this is an impl of A");
};
}
(B.cpp)
namespace foo
{
class BImpl : public B
{
public:
B(std::string other_data) : m_other_data(other_data) {};
~B() {};
void method4()
{
//something really important, can't think of it right now ;-)
};
private:
std::string m_other_data;
};
B* B::gimmeABee()
{
return new BImpl("this is an imll of B");
};
}
Now the compiler complains - rightly so - about the virtual functions
method1() and method2() that I haven't implemented in AImpl and BImpl.
What I want is a TImpl class that both AImpl and BImpl can inherit from
so that I don't have to implement method1() and method2() in two different .cpp files.
Is it possible? Am I out to lunch? Am I asking too many rhetorical questions for a StackExchange post?
Thanks in advance,
Mike
Yeah, it is possible. Common practice is to use the following snippet:
template<typename Interface>
class TImpl : public Interface
{
public:
virtual void method1(std::string a_parameter) { /* implementation */ }
virtual void method2() { /* implementation */ }
};
And then inherit from it as follows:
class Aimpl : public TImpl<A>
{
public:
virtual void method3() { /* implementation */ }
};
class Bimpl : public Timpl<B>
{
public:
virtual void method4() { /* implementation */ }
};
You can put implementation of Timpl in cpp file, but then you have to explicitly instantiate it for every possible interface. This is done as follows in the cpp:
template<typename Interface>
void Timpl<Interface>::method1(std::string a_parameter)
{
/* implementation */
}
template<typename Interface>
void Timpl<Interface>::method2()
{
/* implementation */
}
template class Timpl<A>;
template class Timpl<B>;
Do not make method1 and method2 pure virtual functions. Give them an implementation!
i.e.
declare
class T
{
public:
virtual void method1(std::string a_parameter);
virtual void method2();
};
Define:
void T::method1(std::string a_parameter) { // Could use const/reference here perhaps
....
}
etc...
It seems your problem is the wrong believe that it is better to have only pure virtual functions in your base class! Just get over it and provide a default implementation in T. Although there are religious believes that is bad, there isn't any technical reason not to have it. Insisting in having only pure virtual functions in base classes is like insisting in all music having to be 12-tone music!
since both of the son classes (A & B) don't have (method1, method2) then you don't have to use the virtual. you can easily provide a default implementation in the base class and then invoke and use these methods through the inheritance chain since the inheritance type here is public.
In the A and B classes declaration, the methods 3 and 4 are pure virtual, so in the definition file you shouldn't gives an implementation of them. In my understand A and B are leading the implementation of the T methods and their own virtual methods to other concrete classes who inherit from them. If you thing that A and B are concrete classes, put away the "= 0" from the following lines:
class A : public T
{
public:
virtual ~A() {};
virtual void method3() = 0; // <-- Here
//and a factory function
static A* gimmeAnAyy();
};
class B : public T
{
public:
virtual ~B() {};
virtual void method4() = 0; // <-- and Here
//and a factory function
static B* gimmeABee();
};
If doesn't want to implement the methods 1 and 2 of the T class, give an empty or a default implementation to them, in the T class or in the A and B classes.
But consider this, if you inherits from an interface, is expected that you IMPLEMENT that interface. :)
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).
Consider following hierarchy:
class Interface {
public:
virtual void foo() = 0;
};
class SubInterface: public Interface {
public:
virtual void bar() = 0;
};
class Base: public Interface {
public:
void foo() {};
};
class Impl: public SubInterface, public Base {
public:
void bar() {};
};
There are several sub interfaces which offer other methods in addition to foo().
There can be several implementing classes to a sub interface.
foo() is always implemented the same way.
Here is an example which simulates how these classes would be used:
int main() {
SubInterface* view1 = new Impl(); // Error! Interface::foo() is pure virtual within Impl
view1->foo();
view1->bar();
Interface* view2 = view1;
view2->foo();
}
Why can't the compiler see that Interface::foo() is implemented in Base which Impl inherits from?
I figured that I could implement foo() in Impl explicitly and delegate the call to Base like this:
class Impl: public SubInterface, public Base {
public:
void foo() {
Base::foo();
}
void bar() {};
};
However, I would have to do that for all classes which implement a sub interface, so this way isn't exactly ideal. Is there a better solution?
SubInterface and Base should inherit virtually from Interface otherwise you are creating ambiguity in form of the dreaded diamond
Basically, what happens is that Impl contains two 'instances' of Interface.
Here is why - follow the diagram: