Derived class calling parent class's method that calls an overriden virtual method 调用了错误的方法

Derived class calling parent class's method that calls an overriden virtual method calls the wrong method

首先,对于令人困惑的标题,我们深表歉意。我不知道如何正确措辞。

这个问题不是真正的问题,而是我不知道如何实现的问题。

代码如下:

#include <iostream>

class Parent
{
public:
    virtual void foo()
    {
        std::cout << "Parent's foo" << std::endl;
    }
    void bar()
    {
        foo();
    }
};

class Child : public Parent
{
public:
    void foo()
    {
        std::cout << "Child's foo" << std::endl;
    }
};

int main()
{
    Child c;

    c.bar();

    return 0;
}

当上面的代码运行时,它打印出Child's foo

但是相同的代码,但是 child 类 foo 定义被 void foo(bool def = true) 打印出来 Parent's foo.

如果定义不匹配,是否可以调用 child 的 foo 版本而不是 parent 的 foo 版本?

不幸的是,如果你想添加额外的参数,即使是默认参数,你可以显式创建一个重载函数,它在大多数情况下与调用者的行为相似。

#include <iostream>
class Parent
{
public:
    virtual void foo()
    {
        std::cout << "Parent's foo" << std::endl;
    }
    void bar()
    {
        foo();
    }
};

class Child : public Parent
{
public:
    virtual void foo(bool def) // virtual if another subclass needs to override
    {
        std::cout << "Child's foo def = " << def << std::endl;
    }
    virtual void foo()override //override and virtual optional here
    {
        foo(true);
    }
};

int main()
{
    Child c;

    c.bar();
    c.foo();
    c.foo(true);

    return 0;
}

这纯粹是在 C++ 中重写函数时的函数。

在C++中,函数重写是根据成员函数的"signature"来完成的:

  • 不合格的名字
  • 声明中的确切参数列表(不包括隐式 this
  • this隐式参数的限定

显然,根据定义,this参数的类型不能完全匹配,因为根据定义,类型必须是指向派生的指针class.

[关于参数 cv 限定的注意事项:

调用者看到的声明中的参数必须完全相同,即在删除对象副本上无意义的 cv 限定符之后:这些是本地的 cv 限定符函数体内的变量,这仅在函数定义中有意义。

void f(const int i); // takes an int by value; const is meaningless
void f(int i); // redeclaration of the same function

// redeclaration of the same function yet again
void f(const int ci) { // const is meaningful here
  ci = 1; // error
}

--尾注]

However the same code, BUT with the child classes foo definition being void foo(bool def = true) Prints out Parent's foo.

因为没有参数列表的匹配:一个空的参数列表只匹配一个空的参数列表

你需要在这里用两个重载函数替换默认参数,不转发给另一个:

void foo(bool def); // new function signature 

void foo() { // overrides Parent's member
  foo(true);
}

使用长而复杂的参数列表,可以轻松更改类型并创建新的函数签名,而不是覆盖基础 class 虚函数;大写错误或拼写错误也很容易(想想英语拼写与美国拼写)。通常,错误的函数名称(或任何其他名称:类型名称、模板名称、变量名称...)会导致编译错误,因为未声明具有小拼写更改的名称。但是对于一个成员的原始声明,其意图是覆盖一个基 class 声明,没有任何暗示你试图这样做并且编译器不会警告你(它可能会警告隐藏一个基 class 声明,但这是不同的)。使用 virtual 关键字明确标记旨在覆盖的声明无济于事,引入新的虚函数不是本意。

这是第一版 C++ 标准正式化后的悲惨状况。现在不一样了。

如果您想确定您确实覆盖了基础 class 声明,您现在可以使用 override 关键字:

class Child : public Parent
{
public:
    void foo(bool def);
    void foo() override {
      foo(true);
    }
};