Reputation: 445
I have a situation like below:
class A {
virtual void f() { /*some default impl here*/}
virtual void g() { /*some default impl here*/}
};
class B : public A {
virtual void f() { /* do some specific stuff and call parent's f()*/}
virtual void g() { /* do some specific stuff and call parent's g()*/}
};
class C : public A {
virtual void f() { /* do some specific stuff and call parent's f()*/}
virtual void g() { /* do some specific stuff and call parent's g()*/}
};
class mixed /* I don't know how to derive*/ {
virtual void f() { /* call B::f()*/}
virtual void g() { /* call C::g()*/}
};
I'm thinking about multiple inheritance here. I.e., make mixed
derived from B
and C
.
But there are some known problems (for example,
Diamond problem).
Another solution could be composition.
But what is the correct solution, please advise :)
Thanks in advance!
Upvotes: 3
Views: 142
Reputation: 1424
Here is an alternative to virtual inheritance: using CRTP to mix in the functionality of B
and C
into M
, while sharing a common A
, without the overhead of vtables.
#include <iostream>
struct A
{
int a;
};
template <typename T>
struct B
{
A *get_base_a() {return static_cast<T*>(this);}
void print_a() {std::cout << get_base_a()->a << '\n';}
};
template <typename T>
struct C
{
A *get_base_a() {return static_cast<T*>(this);}
void print_a() {std::cout << get_base_a()->a << '\n';}
};
struct M : A, B<M>, C<M>
{
};
int main()
{
M m;
m.a = 1;
m.B::print_a();
m.C::print_a();
return 0;
}
Note however, that you will not be able to pass an M*
to a function that expects a B*
or a C*
.
Upvotes: 1
Reputation: 264491
some known problems (for example, Diamond problem).
First: There is no diamond pattern unless you explicitly create one.
class mixed: public B, public C
This will make mixed inherit from B and C. With each having their own explicit A (no diamond).
Since both B and C have virtual members derived from A it becomes ambiguous which one should be called but you have solved that by having explicit definitions of all virtuals in mixed
(so problem solved).
void mixed::f() { B::f();} // works fine.
Now even if you explicitly create a diamond.
Note: The diamond pattern does not appear normally. The diamond pattern is a design decision you must explicitly make and you use it to solve certain types of problem (by using virtual inheritance).
class B: public virtual A ...
class C: public virtual A ...
class mixed: public B, public C ...
You still don't have a problem. Because mixed::f()
only uses the B
branch (and then A). While mixed::g()
only uses the C
branch (and then A).
Even if A
has its own state (though that is probably a bad idea, usually best to use interfaces as a virtual base class) then we don't have a problem because mixed::f()
and mixed::g()
only call a function in one child (the problems start to occur if they call both sides and the state of A
is getting mutated by both calls.
Another solution could be composition.
That would also work.
class mixed: public A
{
B b;
C c;
public:
virtual void f() override {b.f();}
virtual void g() override {c.g();}
....
};
But what is the correct solution
There is no correct solution. That will depend a lot on the details that you have not mentioned (like what are the details of A).
BUT the general advice is to prefer composition over inheritance, but that is only general advice specifics will always boil down to the actual problem being solved.
Upvotes: 4
Reputation: 25429
I think you might be looking for something like this. (Sorry, it's a large pile of code but it's really straight-forward.)
#include <iostream>
struct A
{
virtual void
f()
{
std::cout << __PRETTY_FUNCTION__ << '\n';
}
virtual void
g()
{
std::cout << __PRETTY_FUNCTION__ << '\n';
}
// Don't forget the virtual destructor.
virtual ~A() noexcept = default;
};
struct B : virtual A
{
virtual void
f() override
{
std::cout << __PRETTY_FUNCTION__ << '\n';
A::f();
}
virtual void
g() override
{
std::cout << __PRETTY_FUNCTION__ << '\n';
A::g();
}
};
struct C : virtual A
{
virtual void
f() override
{
std::cout << __PRETTY_FUNCTION__ << '\n';
A::f();
}
virtual void
g() override
{
std::cout << __PRETTY_FUNCTION__ << '\n';
A::g();
}
};
struct D : virtual B, virtual C
{
virtual void
f() override
{
std::cout << __PRETTY_FUNCTION__ << '\n';
B::f();
}
virtual void
g() override
{
std::cout << __PRETTY_FUNCTION__ << '\n';
C::g();
}
};
int
main()
{
D d {};
d.f();
std::cout << '\n';
d.g();
}
override
is a C++11 feature to have the compiler check you're actually overriding. Using it is good practice but not required if your compiler doesn't support it. __PRETTY_FUNCTION__
is a GCC extension to get a string literal that names the current function's signature. Standard C++ has __func__
but it's less useful here. You could type the strings yourself if your compiler has no feature comparable to __PRETTY_FUNCTION__
.
Output:
virtual void D::f()
virtual void B::f()
virtual void A::f()
virtual void D::g()
virtual void C::g()
virtual void A::g()
It works, but I don't consider this pretty code. Composition would probably be the better solution here.
Upvotes: 3
Reputation: 234715
The fact that each method has to "do stuff" before calling the parent causes the problem.
One solution would be to have A
and B
as members in the mixed
class. Then you can control what you need to do with them in mixed::f()
and mixed::g()
If you need to, you could create a base class base
with pure virtual functions f()
, and g()
. mixed
can inherit from that, and so could A
, B
and C
. You allude to this possibility when you moot composition.
Upvotes: 3