您的位置:首页 > 编程语言 > C语言/C++

Effective C++ Item 7 为多态基类声明virtual析构函数

2014-10-28 15:43 375 查看
经验1:带多态性质的基类应该声明一个virtual 析构函数。如果class带有任何virtual函数,它就应该拥有一个virtual 析构函数

示例:不为带多态性质的基类声明一个virtual析构函数

[cpp] view
plaincopy





#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析构函数

[cpp] view
plaincopy





#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 析构函数

示例:

[cpp] view
plaincopy





#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 等

示例:

[cpp] view
plaincopy





#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析构函数

[cpp] view
plaincopy





#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析构函数

[cpp] view
plaincopy





#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 析构函数

示例:

[cpp] view
plaincopy





#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 等

示例:

[cpp] view
plaincopy





#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其实是一样道理。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: