上篇文章中,我们介绍了为什么应该彻底避免编写拷贝构造函数和赋值操作符。今天这篇我们讨论下为什么应该避免在析构函数中编写代码。即让析构函数为空。
例如:
virtual ~MyClass() { }
需要编写析构函数可能有如下几个原因:
virtual ~ScppAssertFailedException() throw () { }
class PersonDescription { public: PersonDescription(const char* first_name, const char* last_name) : first_name_(NULL), last_name_(NULL) { if(first_name != NULL) first_name_ = new string(first_name); if(last_name != NULL) last_name_ = new string(last_name); } ~PersonDescription() { delete first_name_; delete last_name_; } private: PersonDescription(const PersonDescription&); PersonDescription& operator = (const PersonDescription&); string* first_name_; string* last_name_; };
class PersonDescription { public: PersonDescription(const char* first_name, const char* last_name) : first_name_(NULL), last_name_(NULL) { if(first_name != NULL) first_name_ = new string(first_name); if(last_name != NULL) last_name_ = new string(last_name); } private: PersonDescription(const PersonDescription&); PersonDescription& operator = (const PersonDescription&); string* first_name_; string* last_name_; };
假设我们决定增加安全检查,检查调用者是否提供了名字和姓氏:
class PersonDescription { public: PersonDescription(const char* first_name, const char* last_name) : first_name_(NULL), last_name_(NULL) { <span style="color:#ff0000;">SCPP_ASSERT(first_name != NULL ,"First name must be provided"); first_name_ = new string(first_name); SCPP_ASSERT(last_name != NULL ,"Last name must be provided"); last_name_ = new string(last_name);</span> } ~PersonDescription() { delete first_name_; delete last_name_; } private: PersonDescription(const PersonDescription&); PersonDescription& operator = (const PersonDescription&); string* first_name_; string* last_name_; };
因此,在前面的例子当中,如果我们假设提供了名字却没有提供姓氏,表示名字的字符串将被分配内存,但永远不会被删除,因此导致了内存泄露。但是,情况还不至于无可挽回。更进一步观察,如果我们有一个包含了其他对象的对象,一个重要的问题是:哪些析构函数将被调用?哪些析构函数将不被调用?
以下是用一个小试验来说明:
class A { public: A() { cout<<"Creating A"<<endl; } ~A() { cout<<"Destroying A"<<endl; } }; class B { public: B() { cout<<"Creating B"<<endl; } ~B() { cout<<"Destroying B"<<endl; } }; class C : public A { public: C() { cout<<"Creating C"<<endl; Throw "Don't like C"; } ~C() { cout<<"Destroying C"<<endl; } private: B b_; };
int main() { cout<<"Testing throwing from constructor."<<endl; try{ C c; }catch(...) { cout<<"Caught an exception."<<endl; } return 0; }
Testing throwing from constuctor. Creating A Creating B Creating C Destroying B Destroying A Caught an exception.
class PersonDescription { public: PersonDescription(const char* first_name, const char* last_name) : first_name_(NULL), last_name_(NULL) { SCPP_ASSERT(first_name != NULL ,"First name must be provided"); first_name_ = new string(first_name); SCPP_ASSERT(last_name != NULL ,"Last name must be provided"); last_name_ = new string(last_name); } ~PersonDescription() { delete first_name_; delete last_name_; } private: PersonDescription(const PersonDescription&); PersonDescription& operator = (const PersonDescription&); <span style="color:#ff0000;">scpp::ScopedPtr<string> first_name_; scpp::ScopedPtr<string> last_name_;</span> };
总结:
从构造函数中抛出异常时为了避免内存泄露,在设计类的时候,使析构函数保持为空函数。
原文:http://blog.csdn.net/kerry0071/article/details/38011067