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

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

问题描述:

我正在使用公开各种纯虚拟接口的外部库。我试图用自己的实现来包装它们,以便扩展它们的功能。我要进入的问题是ChildClass实际上并不来自BaseClass。我写了一个小的应用程序,说明了这一点:覆盖C++中的非虚拟继承

/// 
/// 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. ChildClassImpl范围内执行我自己的Foo()版本并停止从BaseClassImpl派生。

我曾尝试修改外部库以添加虚拟关键字,并且工作得很好。不幸的是,这个库真的是不可修改的,所以这不是一个选项。选项2的作品(这是我现在正在做的),但它最终在一些重复的代码。我不想重新实现一个我已经覆盖的函数。

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

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

+0

为什么你使用虚拟继承? –

+0

正如R Sahu所说,由于您没有提供关于您试图解决的问题的详细信息,因此很难提出替代方案;但一般来说,'ChildClassImpl'应该从'ChildClass'派生出来,并且'ChildClassImpl'中应该定义'BaseClass'方法的任何重写。 –

添加到您的ChildClassImpl

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

这将重定向到BaseClassImpl对ChildClass的FOO(或其BaseClass的复印件)任何电话。

有无论如何覆盖编译器,让它看到Foo已被覆盖在我的父母?

对于您所具有的类层次结构,该语句在继承层次结构的一个分支中不是真实的。

我怀疑你知道如何解决编译器错误。以防万一你不...

添加

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

INT ChildClassImpl解决您的编译器的问题。

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