6.C++虚函数相关(虚函数表,虚函数指针),虚函数的实现原理(热门,重要)虚函数替换过程发生在编译时。虚函数指针是在运行阶段确定的
每个含有虚函数的类都会有至少一个对应的虚函数表。其内存布局为虚函数指针、类内成员变量,其中虚函数指针指向虚函数表(构造过程为:1.拷贝父类虚函数,2.替换子类重写的父类虚函数3.添加子类独有的虚函数)
1.析构函数一般写成虚函数的原因
执行 Base *base = new Base(); 当 delete base时,会调用析构函数为我们释放资源。而 我们执行Base *son = new Son(); 如果Base基类的析构函数不是虚函数的时候,delete son 对象的时候,只会释放Base基类申请的资源,而不是释放Son派生类的资源。原因如下:(总结:将可能会被继承的父类的析构函数设置为虚函数,可以保证当我们new一个子类,然后使用基类指针指向该子类对象,释放基类指针时可以释放掉子类的空间,防止内存泄漏。)
基类指针指向了派生类对象,而基类中的析构函数是非virtual的,而虚构函数是动态绑定的基础。现在析构函数不是virtual的,因此不会发生动态绑定,而是静态绑定,指针的静态类型为基类指针,因此在delete的时候只会调用基类的析构函数,而不会调用派生类的析构函数。这样,在派生类中申请的资源就不会得到释放,就会造成内存泄漏,这是相当危险的:如果系统中有大量的派生类对象被这样创建和销毁,就会有内存不断的泄漏,久而久之,系统就会因为缺少内存而崩溃。
2.那么为什么系统默认的析构函数不是虚函数
因为虚函数需要额外的虚函数表,虚函数指针,占用了额外的内存。只有当需要作为父类的时候才会去考虑构造函数写成虚函数
3.构造函数为什么一般不定义为虚函数
存储空间角度:虚函数对应一个vtable,vtable存储于对象的内存空间,若构造函数是虚的,则需要通过 vtable来调用,若对象还未实例化,即内存空间还没有,无法找到vtable,陷入死循环,出错。
使用角度:虚函数主要用于在信息不全的情况下,能使重载的函数得到对应的调用。
构造函数本身就是要初始化实例,那使用虚函数就没有实际意义
从实际含义上看,在调用构造函数时还不能确定对象的真实类型(因为子类会调父类的构造函数);而且构造函数的作用是提供初始化,在对象生命期只执行一次,不是对象的动态行为,也没有太大的必要成为虚函数
4. 构造函数或者析构函数中调用虚函数会怎样
- 在进入基类构造函数时,调用了基类版本的虚函数
- 在进入基类析构函数时,调用了基类版本的虚函数
,vptr先指向父类的虚函数表,在父类构造完成之后,子类的vptr才指向自己的虚函数表。也就是说,在父类或者子类的构造函数中调用虚成员函数是没有多态性的。
5. 纯虚函数的作用
纯虚函数可以提供抽象类的功能,抽象类则通常提供统一接口的功能,强迫继承它的派生类各自实现符合自己特点的具体功能,并且一定要有这样的功能,这对于设计和架构非常有用