覆盖非虚方法
覆盖非虚方法
假设在Visual C++ 2010中有以下情景:
#include#include 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: Non-virtual display.
Base: Virtual display.
Base: Non-virtual display.
Derived: Virtual display.
因为Base
类的Display
方法不是一个virtual
方法,所以Derived
类不应该能够覆盖它。对吗?
问题是,当我运行这个应用程序时,它打印出这个:
Base: Non-virtual display.
Base: Virtual display.
Derived: Non-virtual display.
Derived: Virtual display.
所以要么我不理解virtual
方法的概念,要么在Visual C++中发生了一些奇怪的事情。
这是怎么回事?
在C++中,如果派生类中存在与基类同名的方法,那么派生类中的方法会隐藏基类中的方法。如果这不是这样的话,那么尝试创建与基类非虚方法同名的方法应该会报错。实际上,这是允许的,并且没有问题。如果直接调用方法,那么会正常调用该方法。
然而,由于非虚函数的特性,C++的方法查找机制不会使用多态性。例如,如果你创建了一个派生类的实例,但是通过基类的指针调用了你的"Display"方法,那么基类的方法会被调用;而对于"vDisplay",派生类的方法会被调用。
下面是一个示例代码:
Base *b = &ba; b->Display(); b->vDisplay(); b = &de; b->Display(); b->vDisplay();
输出结果如下:
Base: Non-virtual display. Base: Virtual display. Base: Non-virtual display. Derived: Virtual display.
在这个例子中,通过基类指针调用方法时,非虚方法会调用基类的方法,而虚方法会调用派生类的方法。
此外,你也可以使用作用域解析语法从派生类实例中调用(非虚)基类方法。
因此,可以肯定地说,无论是否将方法声明为虚方法,都可以在基类中定义一个方法,并在派生类中重写它。唯一的区别是,如果基类指针指向派生类对象,那么如果该方法不是虚方法,将调用基类的方法;如果该方法是虚方法,将调用派生类的方法。没有其他区别。
关于是否仅需更改头文件,还是必须使用"virtual"关键字重新编译源文件的问题,"virtual"关键字只允许在类声明本身之外的定义中使用。因此,不需要将其添加到cpp文件中。但是,你需要重新编译代码。
在C++中,为了实现运行时多态性,需要重写虚函数。虚函数是一种在基类中声明的函数,可以在派生类中进行重写。重写虚函数允许在运行时根据实际对象类型调用相应的函数。
在上述代码中,给出了三种不同类型的函数:纯虚函数、虚函数和普通函数。纯虚函数是在基类中声明的,并且需要在派生类中进行重写。纯虚函数的声明方式为virtual void fun1() = 0
,其中的"= 0"表示该函数是纯虚函数,必须在派生类中进行重写。
虚函数是在基类中声明的函数,可以在派生类中进行重写。虚函数的声明方式为virtual void fun2()
。派生类中可以重写虚函数来提供特定的实现。
普通函数是在基类中声明的函数,不需要在派生类中进行重写。普通函数的声明方式为void fun3()
。普通函数在派生类中不需要进行重写,因为它们不具有多态性。
因此,如果想要实现运行时多态性,需要在派生类中重写虚函数。如果重写了非虚函数或者纯虚函数,将会导致错误。
在C++中,默认情况下,非虚方法在编译时绑定到调用者。这意味着在运行时,无法知道实际指向的对象是什么。因此,只有变量的类型才是重要的,即“Base”。
这种情况下,方法的重写将导致无法实现多态性,因为在编译时已经确定了方法的调用者。如果我们尝试在派生类中重写非虚方法,实际上只是在创建一个新的方法,而不是重写基类的方法。
为了解决这个问题,我们需要将基类的方法声明为虚方法。虚方法允许在派生类中重新定义,并且在运行时选择正确的方法来调用。这样,我们就可以实现多态性,并在运行时确定实际指向的对象。
要将方法声明为虚方法,我们只需要在基类的方法声明前添加关键字“virtual”。例如:
class Base {
public:
virtual void method() {
// 基类的方法实现
}
};
class Derived : public Base {
public:
void method() override {
// 派生类的方法实现
}
};
通过将基类的方法声明为虚方法,并在派生类中重新定义该方法,我们可以实现正确的方法调用并实现多态性。这样,无论指针指向的对象是基类还是派生类,都可以正确地调用适当的方法。