C++虚函数主要是实现多态机制。多态,简单说就是用父类指针指向子类实例。通过父类指针调用实际子类的成员函数。
虚函数定义
虚函数是定义在基类中的函数,子类必须对其覆盖。 语法格式:
virtual void get_number();
虚函数作用
- 基类中函数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
- 基类指针,指向子类对象。并调用子类中的重写的函数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
总结
- 纯虚函数一定没定义,用于规范派生类的接口。包含纯虚函数的类是抽象类,不能定义实例,但可通过声明指向该抽象类具体类的指针或引用。
- 虚函数有定义,基类和子类都有各自版本,调用时动态绑定。
- 虚函数主要就是用于实现多态机制,核心理念就是通过基类访问派生类定义的函数。