【问题标题】:Overriding non-virtual methods覆盖非虚拟方法
【发布时间】:2012-06-19 13:04:40
【问题描述】:

让我们假设 Visual C++ 2010 中的这种情况:

#include <iostream>
#include <conio.h>

using namespace std;

class Base
{
public:
    int b;
    void Display()
    {
        cout<<"Base: Non-virtual display."<<endl;
    };
    virtual void vDisplay()
    {
        cout<<"Base: Virtual display."<<endl;
    };
};

class Derived : public Base
{
public:
    int d;
    void Display()
    {
        cout<<"Derived: Non-virtual display."<<endl;
    };
    virtual void vDisplay()
    {
        cout<<"Derived: Virtual display."<<endl;
    };
};

int main()
{
    Base ba;
    Derived de;

    ba.Display();
    ba.vDisplay();
    de.Display();
    de.vDisplay();

    _getch();
    return 0;
};

理论上,这个小应用的输出应该是:

  • 基础:非虚拟显示。
  • 基础:虚拟显示器。
  • 基础:非虚拟显示。
  • 派生:虚拟显示。

因为 Base 类的 Display 方法不是虚拟方法,所以 Derived 类应该不能覆盖它。对吧?

问题是当我运行应用程序时,它会打印:

  • 基础:非虚拟显示。
  • 基础:虚拟显示器。
  • 衍生:非虚拟显示。
  • 派生:虚拟显示。

所以要么我不理解虚方法的概念,要么在 Visual C++ 中发生了一些奇怪的事情。

有人可以帮我解释一下吗?

【问题讨论】:

  • 你绝对会有 Base: Non-virtual display. 将你的行改为de.Base::Display()

标签: c++ overriding


【解决方案1】:

是的,你有点误会了。

派生类上的同名方法在这种情况下会隐藏父方法。您可以想象,如果不是这种情况,尝试创建与基类非虚拟方法同名的方法应该会引发错误。这是允许的,这不是问题 - 如果您直接调用该方法,就像您所做的那样,它会被调用。

但是,由于是非虚拟的,因此不会使用允许多态性的 C++ 方法查找机制。例如,如果您创建了派生类的实例,但通过指向基类的指针调用了“Display”方法,则会调用基类的方法,而“vDisplay”将调用派生方法。

例如,尝试添加这些行:

Base *b = &ba;
b->Display();
b->vDisplay();
b = &de;
b->Display();
b->vDisplay();

...并按预期观察输出:

基础:非虚拟显示。
底座:虚拟显示器。
底座:非虚拟显示器。
派生:虚拟显示。

【讨论】:

  • 嗨@sje397,感谢您的回复。正如您所说,您可以编写一个通过指向基类的指针调用该方法的示例吗?谢谢!
  • 正如我所说,您还可以使用范围解析语法从派生实例调用(非虚拟)基本方法。
  • 所以,可以肯定的是,我可以在基类中定义一个方法,并在派生类中覆盖它,而不管它是否声明为虚拟。唯一的区别是,如果基指针指向派生类对象,则调用该方法将调用基类的方法(如果它不是虚拟的,则调用派生类的方法)如果它是虚拟的。那正确吗?还有其他区别吗?
  • @Cupidvogel 是的,没错。将其声明为“虚拟”意味着 C++ 将使用机制来支持多态性,并在您通过基类指针调用时检查是否存在该方法的派生版本。我想不出任何其他区别。
  • 只改头文件就够了吗?或者,必须使用“virtual”关键字编译源代码吗?
【解决方案2】:

是的,你误解了一点:

纯虚函数:

virtual void fun1()=0 -> 必须在派生类中重写

虚函数:

virtual void fun2() -> 可以被覆盖

普通函数:

void fun3() -> 不要覆盖它

为了实现运行时多态性,您需要在 c++ 中重写虚函数

【讨论】:

    【解决方案3】:

    我认为在静态与动态绑定的上下文中查看它可能会更好。

    如果该方法是非虚拟的(它在 C++ 中已经是默认的,不像 Java),那么该方法在编译时绑定到它的调用者,这不可能知道将在运行时指向的实际对象。因此,变量类型是最重要的,即“基础”。

    【讨论】:

      猜你喜欢
      • 2018-01-28
      • 1970-01-01
      • 2023-03-27
      • 1970-01-01
      • 2012-10-26
      • 2012-12-08
      • 2011-05-21
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多