在虚函数中使用子类类型参数

时间:2012-09-11 11:58:47

标签: c++ inheritance polymorphism

我有这段代码(由我的现实生活中的麻烦设计)

无法编译,抱怨ExtendsB没有实现B::Run(A* a)。但是,理解A* Run();

的扩展名没有问题
class A { };

class ExtendsA : public A { };

class B
{
public:
    virtual ~B(){}  
    virtual void Run(A* a) = 0;
    virtual A* Run() = 0;
};

class ExtendsB : public B
{
public:
    virtual ~ExtendsB(){}

    // Not OK! It does not see it as an implementation of 
    // virtual void Run(A* a) = 0;
    virtual void Run(ExtendsA* ea) {}; 
    virtual ExtendsA* Run() { return new ExtendsA(); }; // OK
};

为什么C ++允许将返回类型更改为子类,而不是参数类型?

这是一个很好的理由还是仅仅是语言规范中的遗漏点?

4 个答案:

答案 0 :(得分:15)

  

为什么C ++允许将返回类型更改为子类,而不是参数类型?

C ++标准允许您在覆盖虚拟函数时使用 Covariant return type ,但不允许您修改函数参数。是的,有一个好的背后的理由。

  

理由:

覆盖本质上意味着将在运行时调用Base类方法或Derived类方法,具体取决于指针指向的实际对象。
它意味着:
即:“可以通过调用Derived类方法替换调用Base类方法的每个实例,而无需更改调用代码。”

如果上述规则不存在,它将留下一个窗口,通过添加新功能(新派生类)来破坏现有代码。

如果在派生类中有一个函数原型,它与基类虚函数w.r.t参数不同,那么该函数不会覆盖基类函数,因为上面的规则被破坏了。

但是,Covariant返回类型不会破坏此规则,因为向上隐式发生并且Base类指针始终指向派生类对象而不进行任何转换,因此Standard会在返回类型上强制执行协变返回类型的这种情况。

答案 1 :(得分:8)

错误非常明确:您的班级void Run(A*)需要ExtendedB,但您没有。{1}}。你所拥有的只是void Run(ExtendedA*),但那不一样:基类承诺接受任何 A - 指针,但你的ExtendedB::Run更挑剔且只接受一个狭窄的子集。

(你很混淆协方差和逆变,但这与C ++无关,因为C ++不允许反变量覆盖。)

答案 2 :(得分:4)

它只是两种不同的类型,它使两个不同的功能具有两个不同的签名。

通常,如果您正在使用了解C ++ 11的编译器,则应在要覆盖其他函数的函数上使用override关键字。在您的情况下,由于抽象基类,错误变得明显,但在其他情况下,这样的错误会导致大量调试......

答案 3 :(得分:2)

专门使用返回类型会使子类更严格 - 它将充当A。但是,限制Run方法仅接受原始参数的子类会使B不能充当A

继承模型是一种关系。您的代码违反了Liskov Substitution原则。