📄 mi10.htm
字号:
};void BookEntry::cleanup(){ delete theImage; delete theAudioClip;}BookEntry::BookEntry(const string& name, const string& address, const string& imageFileName, const string& audioClipFileName): theName(name), theAddress(address), theImage(0), theAudioClip(0){ try { ... // as before } catch (...) { cleanup(); // release resources throw; // propagate exception }}BookEntry::~BookEntry(){ cleanup();}This is nice, but it doesn't put the topic to rest. Let us suppose we design our BookEntry class slightly differently so that theImage and theAudioClip are constant pointers: class BookEntry {public: ... // as aboveprivate: ... Image * const theImage; // pointers are now AudioClip * const theAudioClip; // const};Such pointers must be initialized via the member initialization lists of BookEntry's constructors, because there is no other way to give const pointers a value (see Item E12). A common temptation is to initialize theImage and theAudioClip like this, // an implementation that may leak resources if an// exception is thrownBookEntry::BookEntry(const string& name, const string& address, const string& imageFileName, const string& audioClipFileName): theName(name), theAddress(address), theImage(imageFileName != "" ? new Image(imageFileName) : 0), theAudioClip(audioClipFileName != "" ? new AudioClip(audioClipFileName) : 0){}but this leads to the problem we originally wanted to eliminate: if an exception is thrown during initialization of theAudioClip, the object pointed to by theImage is never destroyed. Furthermore, we can't solve the problem by adding try and catch blocks to the constructor, because try and catch are statements, and member initialization lists allow only expressions. (That's why we had to use the ?: syntax instead of the if-then-else syntax in the initialization of theImage and theAudioClip.)Nevertheless, the only way to perform cleanup chores before exceptions propagate out of a constructor is to catch those exceptions, so if we can't put try and catch in a member initialization list, we'll have to put them somewhere else. One possibility is inside private member functions that return pointers with which theImage and theAudioClip should be initialized: class BookEntry {public: ... // as aboveprivate: ... // data members as aboveImage * initImage(const string& imageFileName); AudioClip * initAudioClip(const string& audioClipFileName);};BookEntry::BookEntry(const string& name, const string& address, const string& imageFileName, const string& audioClipFileName): theName(name), theAddress(address), theImage(initImage(imageFileName)), theAudioClip(initAudioClip(audioClipFileName)){}// theImage is initialized first, so there is no need to// worry about a resource leak if this initialization// fails. This function therefore handles no exceptionsImage * BookEntry::initImage(const string& imageFileName){ if (imageFileName != "") return new Image(imageFileName); else return 0;}// theAudioClip is initialized second, so it must make// sure theImage's resources are released if an exception// is thrown during initialization of theAudioClip. That's// why this function uses try...catch.AudioClip * BookEntry::initAudioClip(const string& audioClipFileName){ try { if (audioClipFileName != "") { return new AudioClip(audioClipFileName); } else return 0; } catch (...) { delete theImage; throw; }}This is perfectly kosher, and it even solves the problem we've been laboring to overcome. The drawback is that code that conceptually belongs in a constructor is now dispersed across several functions, and that's a maintenance headache.A better solution is to adopt the advice of Item 9 and treat the objects pointed to by theImage and theAudioClip as resources to be managed by local objects. This solution takes advantage of the facts that both theImage and theAudioClip are pointers to dynamically allocated objects and that those objects should be deleted when the pointers themselves go away. This is precisely the set of conditions for which the auto_ptr classes (see Item 9) were designed. We can therefore change the raw pointer types of theImage and theAudioClip to their auto_ptr equivalents: class BookEntry {public: ... // as aboveprivate: ... const auto_ptr<Image> theImage; // these are now const auto_ptr<AudioClip> theAudioClip; // auto_ptr objects};Doing this makes BookEntry's constructor leak-safe in the presence of exceptions, and it lets us initialize theImage and theAudioClip using the member initialization list: BookEntry::BookEntry(const string& name, const string& address, const string& imageFileName, const string& audioClipFileName): theName(name), theAddress(address), theImage(imageFileName != "" ? new Image(imageFileName) : 0), theAudioClip(audioClipFileName != "" ? new AudioClip(audioClipFileName) : 0){}In this design, if an exception is thrown during initialization of theAudioClip, theImage is already a fully constructed object, so it will automatically be destroyed, just like theName, theAddress, and thePhones. Furthermore, because theImage and theAudioClip are now objects, they'll be destroyed automatically when the BookEntry object containing them is. Hence there's no need to manually delete what they point to. That simplifies BookEntry's destructor considerably: BookEntry::~BookEntry(){} // nothing to do!This means you could eliminate BookEntry's destructor entirely.It all adds up to this: if you replace pointer class members with their corresponding auto_ptr objects, you fortify your constructors against resource leaks in the presence of exceptions, you eliminate the need to manually deallocate resources in destructors, and you allow const member pointers to be handled in the same graceful fashion as non-const pointers.Dealing with the possibility of exceptions during construction can be tricky, but auto_ptr (and auto_ptr-like classes) can eliminate most of the drudgery. Their use leaves behind code that's not only easy to understand, it's robust in the face of exceptions, too. Back to Item 9: Use destructors to prevent resource leaksContinue to Item 11: Prevent exceptions from leaving destructors
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -