Try/Catch/ThrowWhich methods can be used to improve software quality?
ReplaceTry/Catch/ThrowThe common practice is to return a response code (sometimes called an error code), for example,Printf (),Scanf ()AndMalloc ()It works like this: the caller tests the return value through the IF statement to determine whether the function is successful.
Although the return code technique is sometimes the most appropriate error handling technique, it adds unnecessaryIfThe annoying effect of the statement.
- Quality downgrade:As we all know, conditional statements may contain about ten times more errors than other types of statements. Therefore, if you can remove conditional statements from the code, you will get more robust code.
- Postponed:Since the condition statements are pivot points, and they are related to the number of test conditions in the white box method test, unnecessary condition statements increase the total test time. If you haven't walked through every pivot point, your code will contain commands that have not been executed in the test until the user/customer finds it, which is terrible.
- Increase development costs:The complexity of unnecessary control processes increases the search for bugs, fixing bugs, and testing.
ThereforeIfTo report errors, useTry/Catch/ThrowFewer bugs, lower development costs, and faster available code.
How to handle constructor failures?
The constructor does not return a type, so it is impossible to return an error code. Therefore, throwing an exception is the best way to mark the failure of the constructor.
If you do not have or do not want to use exceptions, here is a method. If the constructor fails, the constructor can bring the object into a "zombie" state. You can set an internal status bit to make the object as dead, even technically, it is still alive. Then add a query member function, so that users of the class can check this "zombie location" to determine whether the object is actually living or has become a zombie (that is, a "living dead object "). You may want to have another member function to check the zombie space and execute a no-op (or even more annoying) when the object is not actually alive.Abort ()). This is really not beautiful, but if you can't (or don't want to) use exceptions, this is the best method.
How can I handle the failure of the Destructor?
Write a message to the log file. But do not throw an exception!
The C ++ rule is that you cannot throw an exception from the Destructor when another exception is called "Stack unwinding. For example, if someone writesThrow Foo ()Stack will be expanded, and evenThrow Foo ()And} Catch (FOO e ){All stack pages are displayed. This is calledStatck Unwinding)
When the stack is expanded, all partial objects on the stack page are destructed. If one of those destructor throws an exception (assuming it throwsBarObjects), the C ++ runtime system will be unable to determine the situation: should ignoreBarAnd} Catch (FOO e ){End? IgnoreFooAnd find} Catch (Bar e ){ ? No good answer-information is lost for each choice.
Therefore, the C ++ language guarantee callsTerminate ()To kill the process. Sudden death.
A simple way to prevent this situation isDo not throw an exception from the destructor. But if you really want to be smart, you can say"When processing another exception, do not throw an exception from the destructor". However, in the second case, you are in a difficult situation: The Destructor itself requires code processing to throw exceptions and some "other things ", the caller does not guarantee what will happen when the Destructor detects an error (exceptions may be thrown or "other things" may be done "). Therefore, the complete solution is very difficult to write. Therefore, simply do something else ". That is,Do not throw an exception from the destructor.
Of course, these words should not be cited because there are always situations where the rule is invalid ". But in at least 99% cases, this is a good rule.
If the constructor throws an exception, how can I handle the resource?
Each data member in the object should clear itself.
If the constructor throws an exception, the object's destructor will not run. If your object needs to cancel some operations that have already been done (such as allocating memory, opening a file, or locking a semaphore ), these actions to be undone must be remembered by a data member inside the object.
For example, the allocated memory should be assigned to a "smart pointer" member object of the object.FredInstead of allocating memory to uninitializedFred *Data member. In this way, when the smart pointer disappears, the smart pointer destructor will be deleted.FredObject. StandardAuto_ptrThis is an example of the "smart pointer" class. You can also write your own reference count smart pointer.
When someone throws an exception, how do I change the string length of the character array to prevent memory leakage?
If you do need strings, do not useCharArray. It should be replaced by some objects similar to string classes.
For example, if you want to get a copy of a string, modify the copy at will, and add other strings at the end of the modified copy string. The character array method will be as follows:
Void usercode (const char * S1, const char * S2)
{
// Make a copy of S1:
Char * Copy = new char [strlen (S1) + 1];
Strcpy (copy, S1 );
// Now we have a pointer to the allocated free storage memory,
// We need to useTryBlock to prevent memory leakage:
Try {
//... Now we will randomly copy this copy...
// Add S2 to the end of the modified copy:
//... [Re-allocate copy here]...
Char * copy2 = new char [strlen (copy) + strlen (S2) + 1];
Strcpy (copy2, copy );
Strcpy (copy2 + strlen (copy), S2 );
Delete [] copy;
Copy = copy2;
//... At last, we moved the copy randomly again...
} Catch (...){
Delete [] copy;
// Prevent memory leakage when an exception is obtained
Throw;
// Throw the current exception again
}
Delete [] copy;
// Prevent memory leakage when no exception is found
}
Use as followsChar *S is monotonous and prone to errors. Why not use a String object? Your compiler may provide a string class, and it may be better than what you writeChar *S is faster and simpler and safer. For example, if you use the Standardization Committee's string classSTD: StringYour code looks like this:
# Include <string>
// Let the compiler findSTD: StringClass
Void usercode (const STD: string & S1, const STD: string & S2)
{
STD: String Copy = S1;
// Create a copy of S1
//... Now we will randomly copy this copy...
Copy + = S2;
// A adds S2 to the end of the modified copy
//... At last, we moved the copy randomly again...
}
There are only two lines of code in the function body, and 12 lines of code in the previous example. Saving comes from memory management, but some also comes from calls that we don't needStrXxx()Routine. Here are some important points:
- BecauseSTD: StringThe memory management is automatically processed. When the string is increased, we do not need to write any code for memory allocation first.
- BecauseSTD: StringThe memory management is automatically processed and is not required at the end.Delete []Anything.
- BecauseSTD: StringThe memory management is automatically processed, which is not required in the second example.TryBlock, even if someone throws an exception somewhere.