理解(简单?)C++继承

Dan*_*Dan 17 c++ inheritance

我正在努力理解为什么这段代码片段无法编译.

#include <cstdio>

class A {
public:
    virtual int potential()=0;
    virtual int potential(int arg, int arg2)=0;
};

class B : public A {
public:
    int potential() { return 1; }
    virtual int potential(int arg, int arg2) { return 2; }
};

class C : public B {
public:
    int potential(int arg, int arg2) { return 3; }
};


int main(int argc, char** argv) {
    C c;
    int value = c.potential();
    printf("Got %i\n", value);
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

我有两个纯虚方法,都potential在抽象超类中命名A.然后子类B定义两者,但是另一个子类C只需要重新定义其中一个方法.

但是,在编译时,只C识别定义的方法,并且potential()看不到(这应该是继承的B):

In function 'int main(int, char**)':
Line 23: error: no matching function for call to 'C::potential()'
compilation terminated due to -Wfatal-errors.
Run Code Online (Sandbox Code Playgroud)

如果我A::potential(int, int)在继承树中一直重命名为其他东西,例如A::somethingElse(int, int),那么代码编译得很好,输出Got 1就像预期的那样.

这已经使用clang,g ++和MSVC的cl进行了验证.

关于发生了什么的任何想法?

Kon*_*lph 26

但是,在编译时,只识别C中定义的方法,并且看不到potential()(这应该是从B继承的).

C++不工作是这样的:因为你实现了一个不同的potential方法(名称相同的方法,但使用不同的参数)中C,另一种方法是隐藏尽可能C而言.

隐藏是因为C++解析(重载)方法名称的方式:当您potential在类的实例(此处c)上调用方法时,C++会在类中搜索是否存在该名称的方法.如果不是这种情况,它继续在基类中搜索.它在层次结构中更进一步,直到找到该名称的至少一个方法.

但在你的情况下,C++不必搜索远:该方法已经存在C,因此它停止搜索.现在C++尝试匹配方法签名.不幸的是,方法签名不匹配,但此时为时已晚:重载解析失败; C++不会搜索可能匹配的其他方法.

有三种解决方案:

  1. usingC 导入它:

    class C : public B {
    public:
        using B::potential;
        int potential(int arg, int arg2) { return 3; }
    };
    
    Run Code Online (Sandbox Code Playgroud)
  2. 从以下基类实例中调用该方法main:

    C c;
    B& b = c;
    int value = b.potential();
    
    Run Code Online (Sandbox Code Playgroud)
  3. main以下位置明确限定名称:

    C c;
    int value = c.B::potential();
    
    Run Code Online (Sandbox Code Playgroud)


Lig*_*ica 11

问题是名称隐藏.

函数重载和函数继承不是最好的朋友.通常你要么 [嗯,三个人都是什么?]:

  • 在单个类中重载函数.一切正常.
  • 从基类继承非重载函数.一切正常.
  • B派生类中的基类重新实现非重载函数C.C::func 隐藏, B::func因为它具有相同的名称.

你正在使用继承重载,并且你C::func正在隐藏它B::func 并且每次重载它,甚至是那些没有重新实现的C.

这有点古怪的C++混淆,但它很容易解决.

总之,解决的办法是把B::potential()进入的余地Cusing语句:

class C : public B {
public:
    using B::potential; // brings overloads from B into scope
    int potential(int arg, int arg2) { return 3; }
};
Run Code Online (Sandbox Code Playgroud)

在这里写了一篇文章,深入地展示了这个问题.