C++ 虚函数、纯虚函数一文解惑

C++虚函数主要是实现多态机制。多态,简单说就是用父类指针指向子类实例。通过父类指针调用实际子类的成员函数。

虚函数定义

虚函数是定义在基类中的函数,子类必须对其覆盖。 语法格式:

virtual void get_number();

虚函数作用

  1. 基类中函数B调用了函数A,子类重写了函数A,实例化子类,调用函数B,如果不用虚函数重写A,此时会发现子类调用函数B时候最后会调用父类函数A。
#include <iostream>

using namespace std;
// 基类
class Father {

public:
    int a;
    Father(int _a):a(_a){};
    // 不使用虚函数---导致子类调用为父类的函数
    void get_number()
    // 使用虚函数---导致子类调用自己重写的函数
    // virtual void get_number()
    {
        cout<<"Father number a ="<<a<<endl;
    }
    virtual void father_get_number()
    {
        get_number();
    }
};

// 子类
class Son:public Father {
public:
    Son():Father(11){}; // 子类调用基类构造函数
    void get_number()
    {
        cout<<"Son number a ="<< a <<endl;
    }
};

int main(int argc, char* argv[])
{
    Son son;
    son.father_get_number();
}

结果

// 不使用虚函数void get_number()
$ ./main_no_virtual 
Father number a =11
// 使用虚函数 virtual void get_number()
$ ./main_virtual
Son number a =11
  1. 基类指针,指向子类对象。并调用子类中的重写的函数A,如果该函数不是虚函数,最终会调用基类的函数A。
#include <iostream>
using namespace std;
// 基类
class Father {
public:
    int a;
    Father(int _a):a(_a){};
    // 不使用虚函数---导致子类调用为父类的函数
    void get_number()
    // 使用虚函数---导致子类调用自己重写的函数
    // virtual void get_number()
    {
        cout<<"Father number a ="<<a<<endl;
    }
    virtual void father_get_number()
    {
        get_number();
    }
};

// 子类
class Son:public Father {
public:
    Son():Father(11){}; // 子类调用基类构造函数
    void get_number()
    {
        cout<<"Son number a ="<< a <<endl;
    }
};

int main(int argc, char* argv[])
{
    Father *father;
    Son son;
    father = &son; // 基类指针指向子类对象
    father->get_number();
}

结果

// 不使用虚函数void get_number()
$ ./main_no_virtual 
Father number a =11
// 使用虚函数 virtual void get_number()
$ ./main_virtual
Son number a =11

虚函数总结

通过上面的例子,会发现虚函数常常用于子类重写函数,实现多态。如果没有虚函数,你会发现要重写一个基类中的功能是很麻烦的。稍有不慎可能就调用到基类的函数(ps:实际你是想使用子类重写的函数)。 使用虚函数有啥好处呢? 我们可以将基类函数作为系统API,将基类指针作为参数,这样就可以调用我们自定义的子类函数。

纯虚函数定义

纯虚函数是在基类中声明的虚函数,它在基类中没有定义,但要求任何派生类都要定义自己的实现方法。在基类中实现纯虚函数的方法是在函数原型后加 =0 语法格式:

virturl void function()=0;

最上面的两个例子你会发现,基类的函数都使用了虚函数,但是子类就重写了一个get_number()函数。这便是虚函数与纯虚函数的区别。基类中的虚函数,可以被子类重写或不重写,但是纯虚函数是继承该基类的子类必须实现

#include <iostream>

using namespace std;
// 基类
class Father {

public:
    int a;
    Father(int _a):a(_a){};
    // 不使用虚函数---导致子类调用为父类的函数
    // void get_number()
    // 使用虚函数---导致子类调用自己重写的函数
    virtual void get_number(void)=0;

    virtual void reset_number(int)=0;

};

// 子类
class Son:public Father {
public:
    Son(int _a):Father(_a){};
    void get_number(void)
    {
        cout<<"Son number a ="<< a <<endl;
    }
    void reset_number(int number)
    {
        a = number;
        cout<<"Son number a ="<< a <<endl;
    }
};

void pure_virtual_functions(Father *father_ptr, int number)
{
    father_ptr->get_number();
    father_ptr->reset_number(number);
}

int main(int argc, char* argv[])
{
    Son son(18);
    Father *father = &son;
    son.get_number();
    father->get_number();
    pure_virtual_functions(father, 22);

}

结果

Son number a =18
Son number a =18
Son number a =18
Son number a =22

总结

  1. 纯虚函数一定没定义,用于规范派生类的接口。包含纯虚函数的类是抽象类,不能定义实例,但可通过声明指向该抽象类具体类的指针或引用。
  2. 虚函数有定义,基类和子类都有各自版本,调用时动态绑定。
  3. 虚函数主要就是用于实现多态机制,核心理念就是通过基类访问派生类定义的函数。

Jia Feng

我喜欢的东西很贵,想去的地方很远,朝着目标前进。虽然苦,但是我还是会选择那种滚烫的人生

`