经验1:带多态性质的基类应该声明一个virtual 析构函数。如果class带有任何virtual函数,它就应该拥有一个virtual 析构函数
示例:不为带多态性质的基类声明一个virtual析构函数
- #include <iostream>
- #include <string>
- using namespace std;
- class TimeKeeper
- {
- public:
- ~TimeKeeper(){cout << "TimeKeeper destructor" << endl;}
- };
- class AtomicClock: public TimeKeeper{
- ~AtomicClock(){cout << "AtomicClock destructor << endl";}
- };
- class WaterClock: public TimeKeeper{
- ~WaterClock(){cout << "WaterClock destructor << endl";}
- };
- TimeKeeper* getTimeKeeper(string type){
- if(type == "AtomicClock") return new AtomicClock();
- else return new WaterClock();
- }
- int main(){
- TimeKeeper *tk = getTimeKeeper("AtomicClock");
- delete tk;
- system("pause");
- }
TimeKeeper destructor
解析:
当derived class 对象经由一个base class 指针被删除,而该base class带着一个non-virtual析构函数,其结果未有定义——实际执行时通常发生的是对象的derived成分没被销毁,造成资源泄漏。
纠正:为带多态性质的基类声明一个virtual析构函数
- #include <iostream>
- #include <string>
- using namespace std;
- class TimeKeeper
- {
- public:
- virtual ~TimeKeeper(){cout << "TimeKeeper destructor" << endl;} //这里多了个virtual
- };
- class AtomicClock: public TimeKeeper{
- ~AtomicClock(){cout << "AtomicClock destructor" << endl;}
- };
- class WaterClock: public TimeKeeper{
- ~WaterClock(){cout << "WaterClock destructor << endl";}
- };
- TimeKeeper* getTimeKeeper(string type){
- if(type == "AtomicClock") return new AtomicClock();
- else return new WaterClock();
- }
- int main(){
- TimeKeeper *tk = getTimeKeeper("AtomicClock");
- delete tk;
- system("pause");
- }
输出:
AtomicClock destructor
TimeKeeper destructor
经验2:classes的设计目的如果不是作为base classes使用,或不是为了具备多态性,就不该声明virtual 析构函数
示例:
- #include <iostream>
- #include <string>
- using namespace std;
- class Point1{
- public:
- ~Point1(){};
- private:
- int x, y;
- };
- class Point2{
- public:
- virtual ~Point2(){};
- private:
- int x, y;
- };
- int main(){
- Point1 p1;
- Point2 p2;
- cout << sizeof(p1) << endl
- << sizeof(p2) << endl;
- system("pause");
- }
输出:
8
12
解析:
p1的size是8:两个int类型分别是4个字节,所以总共是8个字节
p2的size是12:两个int类型分别是4个字节,共8个字节;有一个virtual函数,所以对象要携带一个虚表指针vptr(virtual table pointer),vptr指向一个由函数指针构成的数组,称为vtbl(virtual table),用来在运行期决定哪一个virtual 函数该被调用。具体关于虚表指针的问题可参见陈皓的博客( http://blog.csdn.net/haoel/article/details/1948051 )。由输出结果可见,无端地将所有classes的析构函数声明为virtual,会增加class的对象的存储空间。因此只有当class内含有至少一个virtual函数时,才要将析构函数声明为virtual
经验3:不要企图继承一个标准容器或者其他包含“non-trivial 析构函数”的class,例如string, vector, list, set 等
示例:
- #include <iostream>
- #include <string>
- using namespace std;
- class SpecialString: public string{
- public:
- ~SpecialString(){cout << "SpecialString destructor" << endl;}
- };
- int main(){
- SpecialString *pss = new SpecialString();
- string *ps = pss;
- delete ps;
- system("pause");
- }
输出:
(空)
解析:
标准string 不含任何virtual函数,delete指向SpecialString类型的指针,只会调用string类的析构函数,不会调用SpecialString的析构函数,现实中*ps的SpecialString资源会泄漏。这跟经验1其实是一样道理。
经验1:带多态性质的基类应该声明一个virtual 析构函数。如果class带有任何virtual函数,它就应该拥有一个virtual 析构函数
示例:不为带多态性质的基类声明一个virtual析构函数
- #include <iostream>
- #include <string>
- using namespace std;
- class TimeKeeper
- {
- public:
- ~TimeKeeper(){cout << "TimeKeeper destructor" << endl;}
- };
- class AtomicClock: public TimeKeeper{
- ~AtomicClock(){cout << "AtomicClock destructor << endl";}
- };
- class WaterClock: public TimeKeeper{
- ~WaterClock(){cout << "WaterClock destructor << endl";}
- };
- TimeKeeper* getTimeKeeper(string type){
- if(type == "AtomicClock") return new AtomicClock();
- else return new WaterClock();
- }
- int main(){
- TimeKeeper *tk = getTimeKeeper("AtomicClock");
- delete tk;
- system("pause");
- }
TimeKeeper destructor
解析:
当derived class 对象经由一个base class 指针被删除,而该base class带着一个non-virtual析构函数,其结果未有定义——实际执行时通常发生的是对象的derived成分没被销毁,造成资源泄漏。
纠正:为带多态性质的基类声明一个virtual析构函数
- #include <iostream>
- #include <string>
- using namespace std;
- class TimeKeeper
- {
- public:
- virtual ~TimeKeeper(){cout << "TimeKeeper destructor" << endl;} //这里多了个virtual
- };
- class AtomicClock: public TimeKeeper{
- ~AtomicClock(){cout << "AtomicClock destructor" << endl;}
- };
- class WaterClock: public TimeKeeper{
- ~WaterClock(){cout << "WaterClock destructor << endl";}
- };
- TimeKeeper* getTimeKeeper(string type){
- if(type == "AtomicClock") return new AtomicClock();
- else return new WaterClock();
- }
- int main(){
- TimeKeeper *tk = getTimeKeeper("AtomicClock");
- delete tk;
- system("pause");
- }
输出:
AtomicClock destructor
TimeKeeper destructor
经验2:classes的设计目的如果不是作为base classes使用,或不是为了具备多态性,就不该声明virtual 析构函数
示例:
- #include <iostream>
- #include <string>
- using namespace std;
- class Point1{
- public:
- ~Point1(){};
- private:
- int x, y;
- };
- class Point2{
- public:
- virtual ~Point2(){};
- private:
- int x, y;
- };
- int main(){
- Point1 p1;
- Point2 p2;
- cout << sizeof(p1) << endl
- << sizeof(p2) << endl;
- system("pause");
- }
输出:
8
12
解析:
p1的size是8:两个int类型分别是4个字节,所以总共是8个字节
p2的size是12:两个int类型分别是4个字节,共8个字节;有一个virtual函数,所以对象要携带一个虚表指针vptr(virtual table pointer),vptr指向一个由函数指针构成的数组,称为vtbl(virtual table),用来在运行期决定哪一个virtual 函数该被调用。具体关于虚表指针的问题可参见陈皓的博客( http://blog.csdn.net/haoel/article/details/1948051 )。由输出结果可见,无端地将所有classes的析构函数声明为virtual,会增加class的对象的存储空间。因此只有当class内含有至少一个virtual函数时,才要将析构函数声明为virtual
经验3:不要企图继承一个标准容器或者其他包含“non-trivial 析构函数”的class,例如string, vector, list, set 等
示例:
- #include <iostream>
- #include <string>
- using namespace std;
- class SpecialString: public string{
- public:
- ~SpecialString(){cout << "SpecialString destructor" << endl;}
- };
- int main(){
- SpecialString *pss = new SpecialString();
- string *ps = pss;
- delete ps;
- system("pause");
- }
输出:
(空)
解析:
标准string 不含任何virtual函数,delete指向SpecialString类型的指针,只会调用string类的析构函数,不会调用SpecialString的析构函数,现实中*ps的SpecialString资源会泄漏。这跟经验1其实是一样道理。