You can use virtual inheritance like shown in the answer of Sam Cristall, but that will make only one Logger
object in an instance of C
, with the side effect that the log will not output the correct name (you also found this problem in a comment of this answer).
Composition
One solution would be to use composition instead of inheritance:
class B
{
private:
Logger log;
// ...
};
Probably the simplest solution. You can see a live example here.
Inheritance
Another solution is to make the base classes different, here by using CRTP. That is:
template<typename T>
class LoggerImp : public Logger
{
public:
LoggerImp(const std::string& n) : Logger(n) {}
};
class B : private LoggerImp<B>
{
private:
typedef LoggerImp<B> LL;
public:
B() : LoggerImp<B>("Class B" ) {}
void doSomethingInB()
{
LL::log("B doing something");
}
};
class C : public B, private LoggerImp<C>
{
private:
typedef LoggerImp<C> LL;
public:
C() : LoggerImp<C>("Class C" ) {}
void doSomethingInC()
{
LL::log("C doing something");
}
};
Some notes:
- I used private inheritance because as I understand your problem the logger is useful only to
B
orC
, not owner of these objects. - The CRTP is used to disambiguate the base names. It is not sufficient but help a lot in your problem
- The typedef is for readability. The fact that the logger bases have different names allows to correctly typedef them, permitting us to call the correct log function.
- A typedef in a derived class hides a typedef in the base class (provided they're for the same name). So you can use the same code for logging in different classes.
- You can also make the
Logger
class directly a CRTP. - You can see a live example here.