C++中的虚函数详解

虚函数在运行时绑定,即运行时确定执行的函数。所谓的后期绑定就是一个基类中有一个虚函数,而派生类中重写了这个函数,那么调用的这个虚函数的时候根据类的实例的不同而调用不同的函数。

实例化是指在面向对象的编程中,把用类创建对象的过程称为实例化。是将一个抽象的概念类,具体到该类实物的过程。实例化过程中一般由类名 对象名 = new 类名(参数1,参数2...参数n)构成。C++中实例化一个类时返回的是新创建的指针,例如Parent *son = new Parent()。其中,类的实例化过程中可以不加入new,区别在于new实例化的对象,在堆上创建,使用完成后,必须delete。而没有使用new实例化的对象,在栈上创建,由内存自由释放。

为什么会有虚函数的存在?

c++发展至今,相信绝大多数类的场景都覆盖掉了。例如,在类的多态中,使用子类实例化对象调用子类的成员函数、使用父类对象调用父类的成员函数,使用子类对象继承类的成员函数,那么,有没有一种方式,可以使用父类的对象调用子类的成员函数呢?于是,虚函数就诞生了,可以让父类指针有多种形态。

用例验证,定义一个基类Father,两个子类Xiaoming与Xiaobai。其中,技能这一项,Father会游泳,Xiaoming会飞行,Xiaobai会喷火。然而对于找女朋友这一项,因为Father已经有妈妈了,所以只能声明为虚函数,交给子类来实现这个方法,Xiaoming想找个漂亮的,Xiaobai成绩不好,想找个学习好的辅导他。具体代码如下:

#include 

using namespace std;

class Father
{
public:
    Father()
    {
        cout << "im a father" << endl;
    }

    virtual void  Skill()
    {
        cout << "can swing" << endl;
    }

    virtual void AskForAGirlFriend() = 0;
};

class Xiaoming : public Father
{
public:
    Xiaoming()
    {
        cout << "im xiaoming" << endl;
    }

    void Skill()
    {
        cout << "can flight" << endl;     // 能飞行
    }

    void AskForAGirlFriend()
    {
        cout << "beautiful" << endl;
    }
};

class Xiaobai : public Father
{
public:
    Xiaobai()
    {
      cout << "im xiaobai" << endl;
    }

    void Skill()
    {
        cout << "can eruption" << endl; // 能喷火
    }

    void AskForAGirlFriend()
    {
        cout << "have a good grades" << endl;
    }
};

int main()
{
    Father *xiaoming = new Xiaoming();
    Father *xiaobai  = new Xiaobai();

    xiaoming->Skill();
    xiaobai ->Skill();

    xiaoming->AskForAGirlFriend();
    xiaobai ->AskForAGirlFriend();

    return 0;
}

输出如下:

im a father    // 使用子类实例化父类对象,优先输出父类的构造函数,再输出子类的构造函数
im xiaoming
im a father
im xiaobai
can flight    // Skill()为虚函数,故输出子类的方法,实现类的多态
can eruption
beautiful     // AskForAGirlFriend()为纯虚函数,故交由子类实现,且基类不可有默认项配置
have a good grades    

总结如下:

1.当基类的某个成员方法,在大多数情形下都应该由子类提供个性化实现,但基类也可以提供一个备选方案的时候,请将其设计为虚函数。例如基类和子类分别有不同的技能。
2.当基类的某个成员方法,必须由子类提供个性化实现的时候,请将其设计为纯虚函数。例如找女朋友这件事,每个人都有不同的标准,不应该统一化。
3.使用一个子类来实例化父类的指针或引用,进而调用经由子类复写的个性化的虚函数,这是C++实现多态性的一个最经典的场景。
4.基类提供的纯虚函数的实现版本,并非为了多态性考虑,因为指向子类对象的基类指针和引用无法调用该版本。纯虚函数在基类中的实现跟多态性无关,它只是提供了一种语法上的便利,在变化多端的应用场景中留有后路;
5.虚函数和普通的函数实际上是存储在不同的区域的,虚函数所在的区域是可被覆盖(也称复写override)的,每当子类定义相同名称的虚函数时就将原来基类的版本给覆盖了,另一侧面也说明了为什么基类中声明的虚函数在后代类中不需要另加声明一律自动为虚函数,因为它所存储的位置不会发生改变,虚函数表可以保证调用对应虚函数。而普通函数的存储区域不会覆盖,每个类都有自己独立的区域互不相干。

你可能感兴趣的:(c++)