Item 07 : 为多态基类声明virtual析构函数
1 #include <iostream> 2 using namespace std; 3 4 class Base { 5 public: 6 Base() : bValue(1) {} 7 virtual ~Base() { cout << "Base destructor" << endl; } 8 // ~Base() { cout << "Base destructor" << endl; } 9 virtual void print() { 10 cout << "print : base" << endl; 11 } 12 private: 13 int bValue; 14 }; 15 16 class Derived : public Base { 17 public: 18 Derived() : dValue(100) {} 19 ~Derived() { 20 cout << "Derived destructor" << endl; 21 } 22 void print() { 23 cout << "print : derived" << endl; 24 } 25 private: 26 int dValue; 27 }; 28 29 int main() { 30 Base* p = new Derived(); 31 p->print(); 32 delete p; 33 }
注意Line 7和8,当基类的析构函数用的是Line7, 即基类析构函数是virtual时,Line32处执行delete p时,相当于调用p所指的析构函数,由于p是一个指向派生类对象的基类指针,且基类中析构函数是virtual的,所以满足了多态的发生条件,即此处调用的是派生类的析构函数Line19。派生类在析构的时候会自动调用其基类的析构函数,所以就有了下图的结果。
假如基类的析构函数不再是virtual的 (即将Line7注释掉,换成Line8),这样在Line32处调用delete p时, p本身是个基类指针,但是所指向的又是一个派生类对象。此时如果p所调用的函数是virtual的,那么p就直接调用派生类中的同名函数,即上面所描述的那样;若p所调用的函数不是virtual的,那它就老老实实的调用基类的函数。
所以将基类的析构函数换成non-virtual之后(即注释掉Line7换成Line8之后),结果如下:
这样导致的结果就是p所指的派生类对象只有基类部分得到释放,派生类对象中独属于派生类的部分仍留在堆上,导致内存泄漏。
==========================================================================================================
1 #include <iostream> 2 using namespace std; 3 4 class Point { 5 public: 6 Point(int xCoord, int yCoord); 7 ~Point(); 8 private: 9 int x, y; 10 }; 11 12 class Point2 { 13 public: 14 Point2(int xCoord, int yCoord); 15 virtual ~Point2(); 16 private: 17 int x, y; 18 }; 19 20 class Point3 { 21 public: 22 Point3(int xCoord, int yCoord) : x(xCoord), y(yCoord) {} 23 ~Point3() {} 24 private: 25 int x, y; 26 }; 27 28 int main() { 29 cout << sizeof(Point) << endl; 30 cout << sizeof(Point2) << endl; 31 Point* p; 32 Point3 obj(1,2); 33 return 0; 34 }
这段代码想要表达两个问题:
1. 虚函数由于vptr,vtbl,会占用一些额外空间。Point和Point2的差别就在于析构函数是否是virtual,输出结果为8,12.
2. 我把Point3这个class放在这的目的是想说明,对于Point和Point2这样的,类内部的构造函数、析构函数只有声明没有定义(都直接以;结尾的),在其成员函数-没有定义的情况下不妨碍对它们求sizeof和定义指针(L31),但是它们不能定义对象(像L32那样)。