Abnormal
For more information on C + + exceptions, please refer to <
http://blog.csdn.net/qianqin_2014/article/details/51325842>
The new C + + exception (exception) mechanism has changed something, and the change is profound, thorough, and probably uncomfortable. For example, the use of unprocessed or raw pointers becomes dangerous; the likelihood of resource leaks increases; it becomes more difficult to write the constructors and destructors that have the behavior you want. Special care is taken to prevent a sudden crash when the program executes. The Execution program and library program size increased, while the speed of operation decreased.
This brings us to the things we know. Many people who use C + + do not know how to use exceptions in a program, and most people don't know how to use it correctly. After the exception is thrown, the behavior of the software is predictable and reliable, and in many ways there is no consistent way to do so. (For a deep understanding of this problem, see Tom Cargill wrote exception handling:a False sense of Security.) For information on the progress of these issues, see the Sutter Exception-safe Generic written by Jack Reeves coping with exceptions and Herb Containers. )
We know that programs can function properly in the presence of anomalies because they are designed as required, not by coincidence. Exception-Safe (EXCEPTION-SAFE) programs are not created by chance. A program that does not design according to the requirements in the case of abnormal operation of the probability and a non-multithreaded design program in the multi-threaded environment of the same probability of normal operation, the probability is 0.
Why use exceptions? Since C has been invented, C programmers have been content with using error codes, so why not have exceptions, especially if the exceptions are problematic as I said above. The answer is simple: exceptions cannot be ignored. If a function represents an exception state by setting a state variable or returning an error code, there is no way to guarantee that the function caller will necessarily detect the variable or test the error code. The resulting program will continue to run from the abnormal state it encounters, the exception is not captured, and the program terminates execution immediately.
C programmers can do similar functions with exception handling only through setjmp and longjmp. But when longjmp is used in C + +, it has some flaws that cannot be called on a local object when it adjusts the stack. (Wq raise, VC + + can guarantee this, but do not rely on this.) While most C + + programmers rely on the invocation of these destructors, setjmp and LONGJMP cannot replace exception handling. If you need a way to inform the non-negligible exception state and search the stack space (searching the stack) to find exception handling code, you also have to make sure that the destructor of the local object must be called, and you need to use C + + exception handling.
Because we already have a lot of knowledge about programming with exception handling, these terms are only an incomplete guide to writing exception-safe (Exception-safe) software. However, they introduce some important ideas to anyone who uses exception handling in C + +. By following these guidelines, you can improve the correctness, robustness and efficiency of your software, and you will avoid many of the problems that you often encounter when using exception handling.
Item M9: Using destructors to prevent resource leaks
Say goodbye to the pointer. You have to admit that you will never like to use pointers.
Ok
you don't have to say goodbye to all the pointers, but you need to say good-bye to the pointers used to manipulate local resources. Suppose you are writing software for a small animal shelter, a small animal shelter is an organization that helps puppy cats find their owners. A daily shelter establishes a document containing information about the host animals it manages on the same day, and your job is to write a program that reads the files and then handles each of the host animals appropriately (appropriate processing).
A reasonable way to complete this program is to define an abstract class, ALA ("adorable Little Animal"), and then build a derived class for puppies and kittens. A virtual function processadoption each species of animal:
Class ALA {public: virtual void processadoption () = 0; ...}; Class Puppy:public ALA {public: virtual void processadoption (); ...}; Class Kitten:public ALA {public: virtual void processadoption (); ...};
You need a function to read the information from the file and then generate a puppy (puppy) object or kitten (kitten) object based on the information in the file. This work is well suited to virtual constructor, and this function is described in detail in article M25. To accomplish our goal, we declare a function like this:
Read the animal information from S, and then return a pointer//pointing to a newly created object of some type Ala * Readala (istream& s);
The key part of your program is this function, as follows:
void Processadoptions (istream& dataSource) { while (dataSource) { ///still has data, continue to cycle ALA *pa = Readala ( DataSource); Get the next animal pa->processadoption (); Handling of host Animals delete pa; Delete the object returned by Readala } }
This function loops through the information in the DataSource and processes each item it encounters. The only thing to remember is to remove the PA at the end of each loop. This is necessary because each call to Readala builds a heap object. If you do not delete the object, the loop generates a resource leak.
Now think about it,
What happens if Pa->processadoption throws an exception? Processadoptions does not catch an exception, so the exception is passed to the caller of Processadoptions. In the pass, all statements after the call to the Pa->processadoption statement in the Processadoptions function are
Skip over, which means that PA was not removed. As a result, any time pa->processadoption throws an exception will cause a processadoptions memory leak.
plugging leaks is easy:
void Processadoptions (istream& dataSource) { while (dataSource) { ALA *pa = Readala (dataSource); try { pa->processadoption (); } catch (...) { //Catch all exceptions delete pa; Avoid memory leaks //Throw when exceptions are thrown ; Transfer exception to caller } delete pa; Avoid resource leaks} //When no exception is thrown}
But you have to make minor changes to your code with try and catch. What's more, you have to write a double purge code, one for the normal run, one for the exception to be prepared. In this case, you must write a two delete code. Like other repetitive code, this code is annoying and difficult to maintain, and it seems to have problems. Whether we let processadoptions return normally or throw an exception, we all need to remove the PA, so why do we have to write delete code in multiple places?
(Wq Raise, VC + + support try...catch...final structure of SEH. )
We can putThe
clean-up code that is always executed is placed in the destructor of the local object within the Processadoptions function, which avoids repeating the cleanup code. Because the local object is always disposed when the function returns, no matter how the function exits. (Only one exception is when you call longjmp.) This drawback of longjmp is the main reason C + + is the first to support exception handling)
The specific method is to replace the pointer PA with an object that behaves like a pointer. When the Pointer-like object (class pointer object) is disposed, we can have its destructor call delete. The object that replaces the pointer is called the Smart Pointers (smart pointer) (in C + + primer ), See article M28 for explanations, you can make Pointer-like objects very dexterous. Here, we don't need such a clever pointer, we just want a Pointer-lik object, and when it leaves the living space it knows to delete the object it points to.
Note: In the fifth edition of C + + Primer, it is clear that auto_ptr, while still part of the standard library, should use UNIQUE_PTR when writing programs.
It is not difficult to write such a class, but we do not need to write it ourselves. The standard C + + library function consists of a class template called Auto_ptr, (
why not apply shared_ptr or unique_ptr? That's what we want. In each of the constructors of the Auto_ptr class, let a pointer point to a heap object, and delete the object in its destructor. Some of the important parts of the Auto_ptr class are shown below:
Template<class t>class auto_ptr {public: auto_ptr (T *p = 0): PTR (P) {} //Save PTR, point to Object ~auto_ptr () {del Ete ptr; } //Delete the object pointed to by PTR private: T *ptr; Raw ptr to Object};
The complete code for the Auto_ptr class is very interesting, and the simplified code implementation described above cannot be applied in practice. (We must add at least the copy constructor, the assignment operator and the pointer-emulating function that will be described in clause M28), but the rationale behind it should be clear: replace the raw pointer with the Auto_ptr object, You will no longer be concerned that the heap object cannot be deleted, even when an exception is thrown, and the object can be deleted in time.
(because Auto_ptr's destructor uses a single-object form of Delete, auto_ptr cannot be used to point to a pointer to an array of objects.) If you want to make auto_ptr similar to an array template, you must write one yourself. In this case, it might be better to use a vector instead of an array. )
use the Auto_ptr object instead of the raw pointer, processadoptions as shown below:
void Processadoptions (istream& dataSource) {while (dataSource) { auto_ptr<ala> pa (Readala ( DataSource)); Pa->processadoption (); }}
This version of Processadoptions differs from the original processadoptions function in two different ways.
First, PA is declared as a Auto_ptr<ala> object, not a raw ala* pointer。
Second, there is no DELETE statement at the end of the loop. The rest is the same, because the Auto_ptr object behaves like a normal pointer in addition to the destructor. is not very easy.
The idea behind auto_ptr is to use an object to store resources that need to be freed automatically, and then rely on the destructor of the object to release resources, not just on pointers, but also in the allocation and release of other resources. Consider a function in a GUI program that needs to create a window to explicitly read some information:
This function will cause a resource leak if an exception is thrown with void DisplayInfo (const information& info) { window_handle W (CreateWindow ()); Explicit information DestroyWindow (W) in the window corresponding to W ;}
Many window systems have c-like interfaces that use the like CreateWindow and DestroyWindow functions to obtain and release window resources. If an exception is thrown when the information is displayed in the corresponding window of W, the window corresponding to W will be lost, just like other dynamically allocated resources.
The workaround is to create a class that allows its constructors and destructors to acquire and release resources, as described earlier:
Class WindowHandle {public: windowhandle (Window_handle HANDLE): W (HANDLE) {} ~windowhandle () {DestroyWindow ( W); } operator Window_handle () {return w;} See belowprivate: window_handle W; The following function is declared private, preventing the creation of multiple window_handle copies //discussions on a more flexible approach see article M28. windowhandle (const windowhandle&); windowhandle& operator= (const windowhandle&);};
This looks somewhat like auto_ptr, except that the assignment operation and copy construction are explicitly forbidden (see clause M27), and an implicit conversion operation can convert WindowHandle to Window_handle. This ability is very important for using WindowHandle objects, because it means you can use the WindowHandle as you would anywhere with raw window_handle. (see article M5 to see why you should be cautious about using implicit type conversion operations)
By giving the WindowHandle class, we are able to rewrite the DisplayInfo function as follows:
If an exception is thrown, this function can avoid resource leaks void DisplayInfo (const information& info) { windowhandle W (CreateWindow ()); Explicit information in the window corresponding to W; }
Even if an exception is thrown within the displayinfo, the window created by CreateWindow can be released.
resources should be encapsulated in an object, following this rule, you can usually avoid resource leaks in the presence of unusual circumstances.。
But what happens if an exception is thrown when you are allocating resources? For example, when you are in the constructor of the Resource-acquiring class. And what happens if an exception is thrown when such a resource is being released?
constructors and destructors require special techniques. You can obtain the relevant knowledge in terms M10 and terms M11.
Summary: Use a smart pointer and place the resource release function in a destructor to prevent memory leaks!
More effective C + +----Exception & (9) Use destructors to prevent resource leaks