在C ++中覆盖非虚拟继承

时间:2015-10-20 03:09:04

标签: c++ inheritance

我正在使用一个公开各种纯虚拟接口的外部库。我试图用我自己的实现包装它们,所以我可以扩展它们的功能。我遇到的问题是ChildClass实际上并非来自BaseClass。我写了一个小应用程序来证明这一点:

///
/// External Library
///
class BaseClass {
  public:
    virtual ~BaseClass() { }
    virtual int Foo(void) = 0;
};

class ChildClass
    : public BaseClass {
  public:
    virtual int Bar(void) = 0;
};

///
/// Internal code
///
class BaseClassImpl
    : public virtual BaseClass {
  public:
    virtual int Foo(void) {
        return 5;
    }
};
class ChildClassImpl
    : public virtual ChildClass
    , public virtual BaseClassImpl {
  public:
    virtual int Bar(void) {
        return 12;
    }
};

int main(int, char* []) {
    ChildClass* impl = new ChildClassImpl;
    printf("%d, %d\n", impl->Foo(), impl->Bar());
    return 0;
}

编译器给我的输出是:

1>main.cpp(41): error C2259: 'ChildClassImpl': cannot instantiate abstract class
1>  main.cpp(41): note: due to following members:
1>  main.cpp(41): note: 'int BaseClass::Foo(void)': is abstract
1>  main.cpp(9): note: see declaration of 'BaseClass::Foo'

从概念上讲,我理解这个问题。编译器看到来自两个不同父项的ChildClassImpl继承BaseClass的“钻石问题”。我可以使用两种解决方案:

  1. ChildClass虚拟派生自BaseClassclass ChildClass : public virtual BaseClass)。这将删除编译器错误并将其转换为警告(main.cpp(38): warning C4250: 'ChildClassImpl': inherits 'BaseClassImpl::BaseClassImpl::Foo' via dominance)
  2. Foo()中实施我自己的ChildClassImpl版本,并停止从BaseClassImpl派生。
  3. 我确实尝试修改外部库以添加虚拟关键字,并且工作得很好。不幸的是,这个库真的是不可修改的,所以这不是一个选择。选项2工作(这是我现在正在做的),但最终会出现在一些重复的代码中。我不想重新实现我已经覆盖的功能。

    我理解虚拟继承告诉编译器确保继承树中只有BaseClass的一个副本,这就是它的工作原理。但是,我不明白在这种情况下这是否真的是一种限制。外部类是纯虚拟的,应该只有一个vtable,每个函数只有一个版本。传统的钻石问题(编译器选择Child1::FooChild1::Foo?)在这里并不存在,因为没有重复的函数。

    我知道在这种情况下正确的答案是“你做错了”,但我无法控制外部库,我需要使用我的Impl,就好像它们是原稿。无论如何都要覆盖编译器以允许它看到Foo已经在我父母中被覆盖了吗?

2 个答案:

答案 0 :(得分:2)

  

是否有覆盖编译器以允许它看到Foo已在我父母中被覆盖?

使用您拥有的类层次结构,该语句在继承层次结构的一个分支中不正确。

我怀疑你知道如何解决编译器错误。万一你没有......

添加

virtual int Foo(void) {
    return BaseClassImpl::Foo();
}

int ChildClassImpl来解决编译问题。

类名通常代表抽象。像BaseChild这样的通用名称并不能提供您正在处理的抽象的线索。如果不理解那些抽象是什么,我很难建议一个有意义的类层次结构。

答案 1 :(得分:0)

添加到您的ChildClassImpl课程

virtual int foo(void) {
    return BaseClassImpl::foo();
}

这会将对ChildClass的foo(或其BaseClass副本)的任何调用重定向到BaseClassImpl。