什么是虚函数
虚函数是 C++ 实现动态单分派子类型多态,通过基类指针访问各个子类
动态:在运行时决定的(相对的是静态,即在编译期决定,如函数重载、模板类的非虚函数调用)
单分派:基于一个类型去选择调用哪个函数(相对于多分派,即由多个类型去选择调用哪个函数)
子类型多态:以子类型-超类型关系实现多态(相对于用参数形式)
指向基类的指针在操作它的多态类对象时,会根据不同的类对象,调用其相应的函数,这个函数就是虚函数。
class A
{
public:
virtual void print(){cout<<"This is A"<<endl;}
};
class B : public A
{
public:
void print(){cout<<"ThisisB"<<endl;}
};
由于这两个类中有虚函数存在,所以编译器就会为他们两个分别插入一段你不知道的数据,并为他们分别创建一个表。那段数据叫做vptr指针,指向那个表。那个表叫做vtbl,每个类都有自己的vtbl,vtbl的作用就是保存自己类中虚函数的地址,我们可以把vtbl形象地看成一个数组,这个数组的每个元素存放的就是虚函数的地址,请看图
析构函数为什么为虚函数
析构函数是释放资源,避免“资源泄漏”,更加安全。
析构函数执行时先调用派生类的析构函数,其次才调用基类的析构函数。如果析构函数不是虚函数,而程序执行时又要通过基类的指针去销毁派生类的动态对象,那么用delete销毁对象时,只调用了基类的析构函数,未调用派生类的析构函数。这样会造成销毁对象不完全。
构造函数为什么不为虚函数
1 构造一个对象的时候,必须知道对象的实际类型,而虚函数行为是在运行期间确定实际类型的。而在构造一个对象时,由于对象还未构造成功。编译器无法知道对象 的实际类型,是该类本身,还是该类的一个派生类,或是更深层次的派生类。无法确定。。。
2 虚函数的执行依赖于虚函数表。而虚函数表在构造函数中进行初始化工作,即初始化vptr,让他指向正确的虚函数表。而在构造对象期间,虚函数表还没有被初 始化,将无法进行。
虚函数的意思就是开启动态绑定,程序会根据对象的动态类型来选择要调用的方法。然而在构造函数运行的时候,这个对象的动态类型还不完整,没有办法确定它到底是什么类型,故构造函数不能动态绑定。(动态绑定是根据对象的动态类型而不是函数名,在调用构造函数之前,这个对象根本就不存在,它怎么动态绑定?)
编译器在调用基类的构造函数的时候并不知道你要构造的是一个基类的对象还是一个派生类的对象。
普通继承
例如C继承与A、B, C的内存排序为,A::Vptr,A的变量,B::vptr,B的变量,然后再是C的变量。C此时不管出现什么虚函数,都不会有自己的虚函数表
虚继承
Vfptr虚函数表指针
Vbptr“虚基类表指针”(vbptr)成员变量,从而达到间接计算虚基类位置的目的。该变量指向一个全类共享的偏移量表,表中项目记录了对于该类而言,“虚基类表指针”与虚基类之间的偏移量。
例如C继承与A、B, C的内存排序为,A::Vfptr(指向虚函数表), A::Vbptr,A的变量,B::vbptr,B::vfptr, B的变量.
如果子类有自己独特的虚函数,增加一个虚函数表,增加一个指针,如果父类有Vbptr,子类直接继承就行