C类继承B类和A类.B类也继承A类

时间:2014-03-13 14:50:16

标签: c++ multiple-inheritance derived-class

我想记录来自基类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'这是不言自明的。

我可以实施哪种不同的模型来实现这一目标?

2 个答案:

答案 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");
    }
};

一些注意事项:

  • 我使用了私有继承,因为我了解您的问题,记录器仅对BC有用,而不是这些对象的所有者。
  • CRTP用于消除基本名称的歧义。这还不够,但在你的问题上有很多帮助
  • typedef是为了便于阅读。记录器基础具有不同名称的事实允许正确地键入它们,允许我们调用正确的日志功能。
  • 派生类中的typedef会在基类中隐藏typedef(前提是它们的名称相同)。因此,您可以使用相同的代码登录不同的类。
  • 您也可以将Logger类直接设为CRTP。
  • 您可以看到a live example here