条款7:为多态基类声明virtual析构函数
栈空间和堆空间:
一个由C/C++编译的程序占用的内存主要分为以下几个部分:1:栈区(stack):编译器自动分配释放,存放函数的参数值、局部变量的值等,其操作方式类似于数据结构的栈。2:堆区(heap):一般是由程序员分配释放,若程序员不释放的话,程序结束时可能由OS回收,分配方式类似于链表。3:全局区(static):存储全局变量和静态变量,初始化的放一个区域,未初始化的放在相邻的另一块区域,程序结束由OS释放。4:文字常量区:常量字符串放在这里,程序结束后由OS释放。5:程序代码区。
堆和栈的区别:一、内存分配方式不同;二、申请和响应不同:1、申请方式:stack由系统自动分配,heap需自己申请;2、申请后系统的响应。栈:只要栈的剩余空间大于所申请的空间,OS将为程序提供内存,否则将报异常提示栈溢出。堆:OS有一个记录内存地址的链表,当系统收到程序的申请时会遍历该链表,寻找第一个空间大于所申请空间的堆结点,然后将该结点从空闲结点链表中删除,并将空间分配给程序,并将多余的部分重新放入空闲链表。三、申请空间的大小限制。栈:是向低地址扩展的一块连续区域,其容量由系统预先规定,所能获得空间较小。堆:向高地址扩展的不连续的数据结构,比较大。四、申请的效率不同:栈是由系统自动分配的,速度快但是无法控制。堆是自己分配的,速度较慢易产生碎片。五、存储内容不同。
在删除堆内存中的基类指针指向子类对象时,如果此时的基类析构函数是non-virtual的,会造成对象的derived成分没被销毁。造成“局部销毁”对象。(消除问题的方法即是将基类的析构函数定义为virtual)
任何class只要带有virtual函数都几乎可以确定应该有一个virtual析构函数,而不被当作基类的类的析构函数不应为virtual(加入虚函数会加入vptr,会使对象的体积增加)。
编译器在幕后对每一个包含虚函数的类或从包含虚函数的类派生的一个类都会为这个类创建一个表(称为vtbl(vitual table)),用于放置虚函数的地址,假如在派生类中没有对基类的虚函数进行重写,则还使用基类虚函数的地址,否则使用派生类重写函数的地址。而每一个这样的类都放置一个指针(称为vptr(virtual table pointer))指向vtbl。因此无论有几个虚函数只需加入一个vptr。vptr在构造函数中完成初始化,且vptr都在对象的开头部分。
不能继承一个标准容器或任何带有“non-virtual"析构函数的class。
为希望成为抽象类声明一个纯虚析构函数较为合理,因为纯虚函数会导致抽象类而无法实体化。
例外情况:如果基类的设计目的不是为了实现多态性则不应声明为virtual析构函数。