Item 26: variable definitions (variable definition) is postponed whenever possible)
By Scott Meyers
Translator: fatalerror99 (itepub's nirvana)
Release: http://blog.csdn.net/fatalerror99/
If you define a variable with a constructor (constructor) or destructor (destructor) type, when the control process reaches the variable definition, you will be subject to the construction cost, when the variable leaves the scope, you will be responsible for analyzing the composition. If this cost is associated with a useless variable, you have to do your best to avoid it.
You may think that you never define useless variables, but maybe you should think about it again. Consider the following function. If a password is long enough, it returns the encrypted version of the password. If the password is too short, the function throws an exception of the logic_error type defined in the Standard C ++ Library (see item 54 ):
// This function defines the variable "encrypted" too soon
STD: String encryptpassword (const STD: string & password)
{
Using namespace STD;
String encrypted;
If (password. Length () <minimumpasswordlength ){
Throw logic_error ("password is too short ");
}
... // Do whatever is necessary to place
// Encrypted version of password in encrypted
Return encrypted;
}
Object encrypted is not in this functionCompletely(Completely) useless, but it is useless if an exception is thrown. That is to say, even if encryptpassword throws an exception, you have to pay for the Construction and Analysis of encrypted. Therefore, you 'd better postpone the encrypted definition until you are sure you really need it:
// This function postpones encrypted's definition until it's truly necessary
STD: String encryptpassword (const STD: string & password)
{
Using namespace STD;
If (password. Length () <minimumpasswordlength ){
Throw logic_error ("password is too short ");
}
String encrypted;
... // Do whatever is necessary to place
// Encrypted version of password in encrypted
Return encrypted;
}
This code is still not as compact as it could have been, because there is no initialization arguments (initializing real parameters) when defining encrypted ). This means that the default constructor (default constructor) will be used. In many cases, the first thing you should do for an object is to give it some values, this can often be done through assignment (Value assignment. Item 4 explains why default-constructing (default constructing) an object is less efficient than initializing it with the value you actually need. That analysis also applies. For example, assume that the core part of encryptpassword is completed in this function:
Void encrypt (STD: string & S); // encrypts s in place
Encryptpassword can be implemented as follows, although it is not the best method:
// This function postpones encrypted's definition
// It's necessary, but it's still needlessly inefficient
STD: String encryptpassword (const STD: string & password)
{
... // Check length as above
String encrypted;// Default-construct encrypted
Encrypted = password;// Assign to encrypted
Encrypt (encrypted );
Return encrypted;
}
One more way to achieve this is to use password to initialize encrypted, thus skipping meaningless and potentially expensive Default Construction (Default Construction ):
// Finally, the best way to define and initialize encrypted
STD: String encryptpassword (const STD: string & password)
{
... // Check Length
String encrypted (password );// Define and initialize
// Via copy constructor
Encrypt (encrypted );
Return encrypted;
}
This suggestion is the true meaning of "as long as possible" ("as long as possible") in the title of this item. You should not only postpone the definition of a variable until the last moment before you have to use it, but also try to postpone its definition until you get its initialization arguments (initialize real parameters ). In this way, you can avoid unnecessary objects construction and destructor, and avoid unnecessary default constructions (default constructions ). Furthermore, initializing them in context where their meanings are already very clear helps document the role of variables.
"But what will happen to the loop ?" You may have such questions. If a variable is only used in a loop, is it better to define it outside the loop and assign a value to it during each loop iteration, Or is it better to define this variable inside the loop? That is to say, which of the following two structures is better?
//Approach:Define outside loop //Approach B:Define inside loop
Widget W;
For (INT I = 0; I <n; ++ I) {for (INT I = 0; I <n; ++ I ){
W = Some value dependent on I;Widget W (Some value dependent on I);
......
}}
Here, I replace a string-type object with a widget-type object to avoid any preference of the cost of constructing, destructing, or assigning values to this object.
For widget operations, the cost of the two methods is as follows:
- Method A: 1 Constructor (constructor) + 1 destructor (destructor) + N assignments (assignment ).
- Method B: N Constructors (constructors) + N Destructors (destructor ).
For classes that cost an assignment (assign value) less than a constructor-destructor pair (constructor/destructor pair), method A is generally more efficient. Especially when n is very large. Otherwise, Method B may be better. In addition, compared with method B, method A makes the name W within a large range (including the range of the loop), which may damage the comprehensibility and maintainability of the program. Therefore, unless you are sure of the following two points: (1) assignment (Value assignment) is cheaper than a constructor-destructor pair (constructor/destructor pair) and (2) you are processing performance-sensitive parts of your code. Otherwise, you should use method B by default.
Things to remember
- Variable definitions (variable definition) is postponed as long as it is possible ). In this way, the definition of the program can be increased and the program performance can be improved.