01 虚函数
- 在类的定义中,前面有
virtual
关键字的成员函数称为虚函数; -
virtual
关键字只用在类定义里的函数声明中,写函数体时不用。
class Base
{
virtual int Fun() ; // 虚函数
};
int Base::Fun() // virtual 字段不用在函数体时定义
{ }
02 多态的表现形式一
- 「派生类的指针(引用)」可以赋给「基类指针(引用)」;
- 通过基类指针(引用)调用基类和派生类中的同名「虚函数」时:
- 若该指针(引用)指向一个基类的对象,那么被调用是 基类的虚函数;
- 若该指针(引用)指向一个派生类的对象,那么被调用 的是派生类的虚函数。
这种机制就叫做“多态”,说白点就是调用哪个虚函数,取决于指针对象指向哪种类型的对象。
在非构造函数,非析构函数的成员函数中调用「虚函数」,是多态!!!
构造函数和析构函数中存在多态吗?
在构造函数和析构函数中调用「虚函数」,不是多态。编译时即可确定,调用的函数是自己的类或基类中定义的函数,不会等到运行时才决定调用自己的还是派生类的函数。
「多态」的关键在于通过基类指针或引用调用一个虚函数时,编译时不能确定到底调用的是基类还是派生类的函数,运行时才能确定。
01 虚函数表
每一个有「虚函数」的类(或有虚函数的类的派生类)都有一个「虚函数表」,该类的任何对象中都放着虚函数表的指针。「虚函数表」中列出了该类的「虚函数」地址。
多出来的 8 个字节就是用来放「虚函数表」的地址。
多态的函数调用语句被编译成一系列根据基类指针所指向的(或基类引用所引用的)对象中存放的虚函数表的地址,在虚函数表中查找虚函数地址,并调用虚函数的指令。
「虚函数表的指针」的作用,「虚函数表的指针」指向的是「虚函数表」,「虚函数表」里存放的是类里的「虚函数」地址,那么在调用过程中,就能实现多态的特性。
// 64位程序指针为8字节
int * p1 = (int *) & a;
int * p2 = (int *) pa;
把 A 类的头 8 个字节的「虚函数表指针」存放到 p1 指针和把 B 类的头 8 个字节的「虚函数表指针」存放到 p2 指针
虚析构函数
析构函数是在删除对象或退出程序的时候,自动调用的函数,其目的是做一些资源释放。
那么在多态的情景下,通过基类的指针删除派生类对象时,通常情况下只调用基类的析构函数,这就会存在派生类对象的析构函数没有调用到,存在资源泄露的情况。
步骤:先调用基类析构函数,然后析构基类,派生类的析构无法再调用。
解决办法:把基类的析构函数声明为virtual
•派生类的析构函数可以 virtual 不进行声明;
•通过基类的指针删除派生类对象时,首先调用派生类的析构函数
,进行delete等处理后调用基类的析构函数,还是遵循「先构造,后虚构」的规则,先析构基类然后再用派生类析构。
包含纯虚函数的类叫抽象类
•抽象类只能作为基类来派生新类使用,不能创建抽象类的对象
•抽象类的指针和引用可以指向由抽象类派生出来的类的对象