More Effective C ++ Terms 9

zhaozj2021-02-08  211

abnormal

C new anomaly (Exception) mechanism has changed certain things, this change is profound, thorough, may be uncomfortable. For example, it is very dangerous to use unprocessed or original pointers. The possibility of resource leaks has increased. The constructor that is written to the behavior you want is more difficult to describe the destructor. Especially careful to prevent the program from being suddenly collapsed. The execution program and the library program size have increased at the same time.

This makes what we know. Many people who use C don't know how to use exceptions in the program, most people don't know how to use it correctly. After the abnormality is thrown, the behavior of the software has predictable and reliability, and there is no consistent approach in many ways to do this. (In order to understand this problem, see the Exception Handling: A False Sense of Security.

We know: The program can run normally in the presence of an abnormality because they are designed in accordance with the requirements, not because of coincidence. Exception-Safe is not accidentally established. A program that is not designed in accordance with the requirement is the same as a program that does not design the multi-threaded requirements in a multi-threaded environment in a multi-threaded environment, and the probability is 0.

Why is anything to use? Since the C language is inventive, C programmers are satisfied with the error code, so why do you have to make an exception, especially if an exception has a problem as I say above. The answer is simple: abnormal cannot be ignored. If a function represents an abnormal state by setting a status variable or returning an error code, there is no way to ensure that the function caller will definitely detect the variable or test error code. The result process will continue to run from the abnormal state it encounter, and the exception is not captured, and the program will immediately terminate execution.

The C programmer can complete similar functions with exceptions only via SetJMP and longjmp. However, when longjmp is used in C , it exists, there is some defects, and when it adjusts the stack, it cannot call the local object call destructor. And most C programmers depend on the call of these destructors, so setJMP and longjmp cannot replace an exception handling. If you need a method, you can notify the abnormal state, and you can't ignore this notification, and search the Searching the Stack to find the exception handling code, you have to ensure that the destructor of the local object must be called, then you It is necessary to use C anomalous processing.

Because we have been designed for programming with exception handling, the following terms are only an incomplete guidance for writing exception-safes software. However, they introduce some important ideas to anyone who uses abnormal processing in C . By paying attention to the following guidance, you can improve your software's correctness, strongness, and efficiency, and you will avoid many problems that are often encountered when using an abnormality.

Terms 9: Prevent resource leakage

Goodbye to the pointer. You must admit that you will never prefer to use pointers.

OK, you don't have to say goodbye to all the pointers, but you need to say goodbye to the pointer used to manipulate the local resources. Suppose you are writing software for a small animal shelter, a small animal shelter is a tissue that helps a puppy kitten looking for the owner. A file established every day, contains information information of the contained animal it managed on the same day, and your job is to write a program to read these files and then properly handle each containment animal (Approode Processing). Completing this program a reasonable way is to define an abstract class, ALA ("Adorable Little Animal", then establish a school for puppies and kittens. A virtual function ProcessAdoption processes individual animals, respectively:

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 information from the file, then generate a Puppy (puppy) object or Kitten object according to the information in the file. This work is ideal for virtual constructors, which describes this function in detail in Terms 25. In order to complete our goal, we declare the function:

// Read the animal information from S, then return a pointer

/ / Point to a newly established type object

Ala * Readala (istream & s);

The key part of your program is this function as follows: Void ProcessadOptions (istream & Datasource)

{

While (DataSource) {//, continue to cycle

Ala * PA = Readala (Datasource); // Get the next animal

PA-> processadoption (); // Treatment of container animals

Delete Pa; // Delete objects returned by Readala

}

}

This function loops traversed the information within the DataSource to process each item it encountered. The only thing to remember is to delete the PS at each cycle end. This is a must because each call readala is built. If you do not delete an object, the loop will generate resource leaks.

Now think about it, if PA-> ProcessAdoption throws an exception, what will happen? ProcessadOptions did not capture exceptions, so exceptions will be passed to the caller of ProcessAdoptions. In the transfer, all the statements after calling the PA-> ProcessAdoption statement in the ProcessadOptions function are jumped, which is that PA is not deleted. As a result, any time PA-> ProcessadOption throws an exception causes ProcessAdoptions memory leakage.

Blocking leaks is easy,

Void ProcessadOptions (istream & Datasource)

{

While (Datasource) {

ALA * PA = Readala (Datasource);

Try {

PA-> ProcessadOption ();

}

Catch (...) {// captures all exceptions

Delete Pa; // Avoid memory leakage

// When an exception is thrown

Throw; // Transport anomalies to the caller

}

Delete Pa; // Avoid resource leakage

} // When there is no abnormality thrown

}

But you must use TRY and CATCH to make small changes to your code. More importantly, you must write double clear code, one for normal run, one is prepared for an exception. In this case, two DELETE codes must be written. Like other duplicate code, this code writes upset and difficult to maintain, and it seems to have problems. Whether we let processadOptions returning normally or throw an exception, we need to delete PA, so why do we have to write a delete code in multiple places?

We can put the total clear code in the ProcessAdOptions function in the destructive syndrome, which avoids repeated writing clear code. Because the local object is always released when the function returns, regardless of how the function exits. (Only one exception is when you call longjmp.longjmp's shortcomings is the main reason for C to support exception processing)

The specific method is to use an object instead of the pointer PA, and the behavior of this object is similar to the pointer. When the Pointer-Like object is released, we can call DELETE in the destructor. The object of replacing the pointer is called the Smart Pointers, see the explanation of the Terms 28, which can make the Pointer-Like objects very dexterously. Here, we don't need such a smart pointer, we only need a Pointer-Lik object that knows the object that deletes it points to the object when it leaves the living space.

It is not difficult to write such a class, but we don't need to write yourself. The standard C library function contains a class template called Auto_PTR, which is what we want. In each Auto_PTR class constructor, let a pointer points to a heap object and delete this object in its destructor. The following is some of the important parts of the Auto_PTR class:

Template

Class auto_ptr {

PUBLIC:

Auto_PTR (T * p = 0): PTR (P) {} // Save PTR, point to object

~ Auto_PTR () {Delete Ptr;} // Delete the PTR pointing to the object

Private:

T * ptr; // raw ptr to object

}

The full code of the AUTO_PTR class is very interesting, and the simplified code implementation cannot be applied in practice. (We must at least plus copy constructor, assigning Operator and the Pointer-Emulating function that will be told in terms 28), but the principle contained behind it should be clear: replacing the RAW pointer with the auto_ptr object, you will no longer Objects cannot be deleted, even when the abnormality is thrown, the object can be deleted in time. (Because Auto_Ptr's destructor uses delete in single object form, Auto_Ptr cannot be used to point to the pointer of the object array. If you want Auto_PTR to be similar to an array template, you must write one. In this case, Vector instead of Array may be better)

Using the Auto_PTR object instead of the RAW pointer, ProcessadOption is as follows: Void ProcessadOptions (Istream & Datasource)

{

While (Datasource) {

Auto_Ptr Pa (Readala (Datasource));

PA-> ProcessadOption ();

}

}

This version of ProcessadOptions is different from the original ProcessadOptions function. First, PA is declared as an Auto_Ptr object, not a RAW ALA * pointer. Second, there is no DELETE statement at the end of the cycle. The rest are the same, because in addition to the descent, the behavior of the Auto_Ptr object is like a normal pointer. Is it easy.

The idea of ​​hiding in auto_ptr is: Store the resource that needs to be released automatically with an object, then rely on the destructive function of the object to release resources, this kind of thinking is not just possible to use on the pointer and can be used in other resources. Release. Think about the function in the GUI program, it needs to create a Window to explicit some information:

// This function will have resource leaks, if an exception thrown

Void DisplayInfo (Const Information & Info)

{

Window_handle w (CREATEWINDOW ());

Smooth information in WINDOW corresponding to W

DESTROYWINDOW (W);

}

Many Window systems have a C-Like interface that uses the Like CreateWindow and DestroyWindow functions to get and release Window resources. If information is displayed in W, an exception is thrown, WINDOW corresponding to W will be lost, just like other dynamically allocated resources.

The solution is the same as previously described, establish a class, allowing its constructor to get and release resources with the destructor:

// A class, get and release a Window handle

Class windowhandle {

PUBLIC:

WindowHandle: w (Handle) {}

~ Windowhandle () {destroywindow (w);}

Operator window_handle () {return w;} // see below

Private:

WINDOW_HANDLE W;

// The following function is declared as private, preventing multiple window_handle copies

// For a more flexible discussion, please refer to Terms 28.

WindowHandle (Const WindowHandle &);

WindowHandle & Operator = (Const WindowHandle &);

}

This looks like Auto_PTR, just assignment operations and copy constructs explicitly disabled (see Terms 27), with an implicit conversion operation to convert WindowHandle to Window_Handle. This capability is very important for using the WindowHandle object, because this means you can use WindowHandle as anywhere like RAW Window_Handle. (See Terms 5. Learn why you should carefully use implicit type conversion operations)

By giving the WindowHandle class, we can rewrite the DisplayInfo function as follows:

// If an exception is thrown, this function can avoid resource leakage

Void DisplayInfo (Const Information & Info) {

WindowHandle W (CREATEWINDOW ());

Obsessed information in WINDOW corresponding to W;

}

Even if an exception is thrown in the DISPLAYINFO, Window established by CreateWindow can also be released.

Resources should be packaged in an object, follow this rule, you usually avoid resource leaks in the abnormal environment. But if you are allocating a resource, an exception is thrown, what happens? For example, you are in the construnce function of the Resource-Acquiring class. Also if such resources are being released, an exception is thrown, what happens? Constructor requires special techniques for constructor and destructive functions. You can get relevant knowledge in Terms 10 and Terms 11.

转载请注明原文地址:https://www.9cbs.com/read-1001.html

New Post(0)