我正在学习C++,我刚刚开始学习虚拟函数。

从我(在书中和网上)读到的内容来看,虚拟函数是基类中的函数,可以在派生类中重写。

但在本书的早些时候,当我学习基本继承时,我能够在派生类中重写基函数,而不使用虚函数。

那么我在这里错过了什么?我知道虚拟函数还有很多,它似乎很重要,所以我想清楚它到底是什么。我只是在网上找不到一个直截了当的答案。


当前回答

您必须区分重写和重载。如果没有virtual关键字,则只能重载基类的方法。这只意味着隐藏。假设您有一个基类base和一个派生类Specialized,它们都实现了void foo()。现在有一个指向Base的指针指向Specialized的实例。当您对其调用foo()时,您可以观察到virtual的不同之处:如果该方法是虚拟的,则将使用Specialized的实现,如果缺少,则将选择Base的版本。最好不要重载基类中的方法。使方法非虚拟化是作者告诉你它在子类中的扩展不是有意的。

其他回答

您需要至少1个级别的继承和一个升级来演示它。下面是一个非常简单的示例:

class Animal
{        
    public: 
      // turn the following virtual modifier on/off to see what happens
      //virtual   
      std::string Says() { return "?"; }  
};

class Dog: public Animal
{
    public: std::string Says() { return "Woof"; }
};

void test()
{
    Dog* d = new Dog();
    Animal* a = d;       // refer to Dog instance with Animal pointer

    std::cout << d->Says();   // always Woof
    std::cout << a->Says();   // Woof or ?, depends on virtual
}

关于效率,虚拟函数的效率略低于早期绑定函数。

“这种虚拟调用机制的效率几乎可以与“正常函数调用”机制一样高(25%以内)。它的空间开销是一个具有虚拟函数的类的每个对象中的一个指针加上每个此类类的一个vtbl”[Bjarne Stroustrup的C++教程]

您必须区分重写和重载。如果没有virtual关键字,则只能重载基类的方法。这只意味着隐藏。假设您有一个基类base和一个派生类Specialized,它们都实现了void foo()。现在有一个指向Base的指针指向Specialized的实例。当您对其调用foo()时,您可以观察到virtual的不同之处:如果该方法是虚拟的,则将使用Specialized的实现,如果缺少,则将选择Base的版本。最好不要重载基类中的方法。使方法非虚拟化是作者告诉你它在子类中的扩展不是有意的。

为什么我们需要虚拟功能?

虚拟函数避免了不必要的类型转换问题,我们中的一些人会争论,当我们可以使用派生类指针来调用派生类中特定的函数时,为什么需要虚拟函数!答案是,它否定了在大型系统开发中继承的全部思想,因为在大型系统中,非常需要使用单指针基类对象。

让我们比较以下两个简单的程序,以了解虚拟函数的重要性:

无虚拟功能的程序:

#include <iostream>
using namespace std;

class father
{
    public: void get_age() {cout << "Fathers age is 50 years" << endl;}
};

class son: public father
{
    public : void get_age() { cout << "son`s age is 26 years" << endl;}
};

int main(){
    father *p_father = new father;
    son *p_son = new son;

    p_father->get_age();
    p_father = p_son;
    p_father->get_age();
    p_son->get_age();
    return 0;
}

输出:

Fathers age is 50 years
Fathers age is 50 years
son`s age is 26 years

具有虚拟功能的程序:

#include <iostream>
using namespace std;

class father
{
    public:
        virtual void get_age() {cout << "Fathers age is 50 years" << endl;}
};

class son: public father
{
    public : void get_age() { cout << "son`s age is 26 years" << endl;}
};

int main(){
    father *p_father = new father;
    son *p_son = new son;

    p_father->get_age();
    p_father = p_son;
    p_father->get_age();
    p_son->get_age();
    return 0;
}

输出:

Fathers age is 50 years
son`s age is 26 years
son`s age is 26 years

通过仔细分析这两个输出,可以理解虚拟函数的重要性。

你熟悉函数指针吗?虚拟函数也是一个类似的想法,只是您可以轻松地将数据绑定到虚拟函数(作为类成员)。将数据绑定到函数指针并不容易。对我来说,这是主要的概念区别。这里的许多其他答案只是说“因为…多态性!”