FAQ 20.11 How can a local object be destructed before the end of its function?

graphics/new_icon.gif

The most important message is to not explicitly call a local object's destructor. The language guarantees that the object's destructor will be called again at the close of the scope in which the local was created (see FAQ 20.04). So if the destructor is called explicitly, it will be called again at the close of the scope, which can have disastrous results. Bang; you're dead.

The easiest way to make sure a local object is destructed before the end of its scope is to insert a pair of braces (that is, a new scope) so that the object will be destructed at the right time. For example, suppose the (desirable) side effect of destructing a local File object is to close the File. Now suppose a local File object needs to be closed before the end of the scope (i.e., the }) of its function. In this case simply add an extra pair of braces to scope the lifetime of the object:

 void sample() {   // ...   {                                                  <-- 1     File x;     // ...                                           <-- 2     // ...   }                                                  <-- 3   // ...                                             <-- 4 } 

(1) Added to limit the scope of File x

(2) This code will execute while File x is still open

(3) File x will close here

(4) This code will execute after File x is closed

In cases where this extra pair of braces cannot be added, add an extra member function that causes the destructor's desirable side effects to occur. This member function should mark the object so that the destructor, which will inevitably be called at the close of the object's scope, will be able to tell if the side effects have already happened. For example, a close() member function could be added to the File class, and that member function could be called where the file should be closed:

 void sample() {   // ...   File x;   // ...                                             <-- 1   x.close();                                         <-- 2   // ...                                             <-- 3 } 

(1) This code will execute while File x is still open

(2) File x will close here

(3) This code will execute after File x is closed

The close() member function could mark the object so the destructor knows not to reclose the file, such as setting the underlying file handle to some invalid value such as -1. To avoid duplication of code in the destructor and the close() member function, the destructor could simply call the close() member function, and the close() member function could check to see if the file handle is in the "already closed" state.

 #include <iostream> #include <stdexcept> using namespace std; int openFile(const char* name) throw() {   // Normally this code would actually open the named file.   // For this example, we pretend everything is handle 42.   int handle = 42;   cout << "opening " << name << " as #" << handle << '\n';   return handle; } void closeFile(int handle) throw() {   // Normally this code would actually close the file   cout << "closing file #" << handle << '\n'; } class File { public:   File(const char* name) throw();   ~File() throw();   void close() throw(); protected:   enum { closed_ = -1 };                             <-- 1   int handle_; private:   // These are never defined; copy semantics are ill defined   File(const File&);   File& operator= (const File&); }; inline void File::close() {   if (handle_ != closed_) closeFile(handle_);   handle_ = closed_; } inline File::File(const char* name) throw() : handle_( openFile(name) ) { } inline File::~File() throw() { close(); } void userCode(bool throwIt) throw(runtime_error) {   File x("sample.txt");   cout << "after open, before throw-or-close\n";   if (throwIt)      throw runtime_error("note that the file still gets closed!");   x.close();   cout << "after close\n"; } int main() {   cout << "====== without throwing an exception ======\n";   userCode(false);   cout << "====== with throwing an exception =========\n";   try {     userCode(true);   }   catch (exception& e) {     cout << "exception caught; " << e.what() << '\n';   } } 

(1) File::closed_ is a constant

The output of this program follows.

 ====== without throwing an exception ====== opening sample.txt as #42 after open, before throw-or-close closing file #42 after close ====== with throwing an exception ========= opening sample.txt as #42 after open, before throw-or-close closing file #42 exception caught; note that the file still gets closed! 

It is important to note that closeFile() is called exactly once per open file, whether or not an exception is thrown or the x.close() instruction is reached. Even if x.close() were explicitly called twice, the underlying file would only be closed once.



C++ FAQs
C Programming FAQs: Frequently Asked Questions
ISBN: 0201845199
EAN: 2147483647
Year: 2005
Pages: 566
Authors: Steve Summit

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net