我想记录来自基类B和派生类C的消息,区别于哪个类记录了消息:
#include "iostream"
class Logger {
public:
Logger(std::string name) : name_(name) {}
void log(std::string msg) { std::cout << name_ << ": " << msg << std::endl; }
private:
std::string name_;
};
class B : public Logger {
public:
B() : Logger("Class B" ) {}
void doSomethingInB() {
log("B doing something");
}
};
class C : public B, public Logger {
public:
C() : Logger("Class C" ) {}
void doSomethingInC() {
log("C doing something");
}
};
int main() {
B* b = new B();
C* c = new C();
b->doSomethingInB(); // I want this to output: Class B: B doing something
c->doSomethingInC();
c->doSomethingInB(); // I also want this to output: Class B: B doing something
return 0;
}
我收到错误&#34;由于歧义[默认情况下已启用]&#34;我在'C'中无法访问基座'Logger'这是不言自明的。
我可以实施哪种不同的模型来实现这一目标?
答案 0 :(得分:3)
编辑:为了响应您对c->doSomethingInB();
需要打印Class B: B doing something
的要求,请考虑合成而非继承,因为您需要B作为自己的子类,其记录器实际上与C不同(除非这里有更多的要求)。例如:
class Logger {
public:
Logger(std::string name) : name_(name) {}
void log(std::string msg) { std::cout << name_ << ": " << msg << std::endl; }
private:
std::string name_;
};
class B {
public:
B() : logger_("Class B" ) {}
void doSomethingInB() {
logger_.log("B doing something");
}
private:
Logger logger_;
};
class C : public B {
public:
C() : logger_("Class C" ) {}
void doSomethingInC() {
logger_.log("C doing something");
}
private:
Logger logger_;
};
答案 1 :(得分:1)
您可以使用如Sam Cristall的答案中所示的虚拟继承,但这将在Logger
的实例中只生成一个C
对象,副作用是日志不会输出正确的名称(你也在这个答案的评论中发现了这个问题)。
一种解决方案是使用组合而不是继承:
class B
{
private:
Logger log;
// ...
};
可能是最简单的解决方案。您可以看到live example here。
另一个解决方案是通过使用CRTP使基类不同。那就是:
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");
}
};
一些注意事项:
B
或C
有用,而不是这些对象的所有者。Logger
类直接设为CRTP。