【问题标题】:C++ Combining overloading and overriding results in strange behaviourC ++结合重载和覆盖会导致奇怪的行为
【发布时间】:2012-09-18 23:09:52
【问题描述】:

考虑下面的代码

class X{
public:
    virtual void foo(X x){  }
    virtual void foo(int index){ }

};

class Y : public X{
public:

    void foo(int index){ }
};


int main(){
    Y y;
    y.foo(X()); //Error, see below
}

X 类重载了虚拟foo 方法。一个版本采用X,另一个采用int。现在类Y 继承自X 并覆盖方法foo(int)foo(X) 方法不能被覆盖,它应该保持不变。

但是,当在main 方法中创建Y 类型的对象并调用foo(X) 时,编译器会报错:

In function ‘int main()’:
error: no matching function for call to ‘Y::foo(X)’
note: candidate is:
note: virtual void Y::foo(int)
note:   no known conversion for argument 1 from ‘X’ to ‘int’

因此,唯一的候选者是被覆盖的foo(int) 方法。似乎另一种方法已经消失了。如果我删除了覆盖版本,即将Y 声明为public Y : public X{};,那么一切正常。为什么会这样?

【问题讨论】:

    标签: c++ overloading overriding


    【解决方案1】:

    当派生类定义的成员与基类中的成员名称​​相同时,派生类名称会隐藏基类名称。

    在您的情况下,函数 Y::foo 隐藏 X::foo。你需要把它带入Y的范围内:

    class Y : public X{
    public:
    
        using X::foo;  //it brings X::foo into the scope of Y
    
        void foo(int index){ }
    };
    

    【讨论】:

    • @gexicide:实际上它并没有违反 LSP,因为当您通过基类的指针或引用访问派生类时,所有基类的方法照常可用。
    • 对术语的一点澄清:当一个函数具有相同的名称和参数列表和相同的返回类型(模协变引用和指针)。名称隐藏只是在派生类中定义与基类中的东西同名的东西;它不依赖于覆盖,也不限于函数。 int foo; in Y 也会隐藏Xfoo 成员。
    • @gexicide:我承认我没有读过 Liskov 的论文,但我确信重要的是派生类的对象是否可以替换基类的每个对象在不违反任何合同的情况下。 LSP 的全部意义在于,遵守它的派生类可以安全地被 只知道基类的代码使用(即不包含任何具有静态类型引用的引用的代码-到你的派生类)。
    • @gexicide:即使 LSP [或其在继承上下文中的常见重新解释] 是一个牢不可破的规则(它不是),或者重要到足以迫使服从,即使以牺牲“脆弱的基类”问题(这是有争议的),直到 C++ 对象模型发明多年后才流行起来。到那时,改变语言的基本行为已经太晚了。
    • 但是现在假设我有不同的重载和不同的可见性(例如,受保护的和公共的)。通过使用using X::foo,我将所有这些都以公共可见性拉到Y 的范围内,这不是我想要的。如何保持重载方法的可见性?
    【解决方案2】:

    重载适用于在同一范围内定义的名称。所以fooY中的foo不会重载X类中的foos,因为它们是在不同的范围内定义的。这被非正式地称为“名称隐藏”。要将基类名称拉入派生类,请添加 using 指令:

    using X::foo;
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-06-09
      • 1970-01-01
      • 2018-08-05
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多