Parameter transfer in c ++
As we all know, there are three parameter transfer methods when calling a function in C ++:
(1) call value transfer;
(2) Address Transfer call (pointer transfer );
(3) Transfer references;
In fact, there is also a parameter transfer method, that is, the global variable transfer method. The "Global" variable here is not necessarily truly global, and all code can be accessed directly, as long as the scope of this variable is sufficient for these two functions to access, for example, two member functions in a class can use one member variable to implement parameter transfer, use static keyword to define, or use namespace for restrictions, in this sense, the member variables can be called "Global" variables (there are no other words better than "Global" for the time being ). Of course, you can use a real global variable outside the class to implement parameter transfer, but sometimes it is not necessary. In engineering terms, the smaller the scope, the better. What are the advantages of this method?
High Efficiency!
Indeed, this efficiency is the most efficient among all parameter transmission methods, which is higher than the previous three methods, No matter under what circumstances. However, this method has a fatal weakness, that is, it does not support multithreading well. If two processes call the same function at the same time and pass parameters through global variables, this function cannot always get the desired result.
Next we will discuss the above three function transfer methods.
1. functional. When passing by value, the real parameter is copied and used in the function body. When modifying the parameter variable in the function body, the real parameter is modified, real parameters are not changed, so if you want to modify the value of real parameters in the called function, using value transfer cannot achieve the goal, then you can only use reference or pointer to pass. For example, two values must be exchanged.
Void swap (int a, int B );
Void main (){
Int a = 1, B = 2;
Swap (a, B );
}
In this way, the values of a and B in the main () function are not actually exchanged. If you want to exchange values, you can only use pointer transmission or reference transmission. For example:
Void swap (int * pa, int * pb );
Or
Void swap (int & ra, int & rb );
2. transfer efficiency. The transfer efficiency here refers to the process in which the code that calls the called function passes the real parameters to the called function. As in the code above, this process is a in main () of the function, B is passed to the function swap. This efficiency cannot be generalized. For the built-in int, char, short, long, float and other data types of 4 bytes or less, in fact, only 1-4 bytes are required for transmission, when pointer transmission is used, 32-bit pointers are transmitted in 32-bit CPUs, and 4 bytes are all an instruction. In this case, value transmission is the same as pointer transmission, when transmitting 8 bytes of data, such as double and long, in 32-bit CPU, the transfer efficiency is slower than the transfer pointer, because 8 bytes need to be obtained twice. On a 64-bit CPU, the efficiency of data transfer and data transfer is the same. Besides, the reference transfer depends on the specific implementation of the compiler. The most obvious implementation method of the reference transfer is to use pointers. In this case, the efficiency is the same as that of pointers, in some cases, the compiler can be optimized and the direct addressing method is adopted. In this case, the efficiency is faster than the value transfer and address transfer calls, it is equivalent to the efficiency of using global variables.
In addition, the custom data types are defined by class and struct. These data types generate a temporary object for value transfer and execute the constructor when the temporary object is destroyed, if the constructor and destructor execute many tasks, or the size of the objects to be passed is relatively large, the consumption of value passing calls is relatively large. In this case, the efficiency of using an address transfer call is the same as that of using a reference transfer call. As mentioned above, in some cases, the reference transfer may be optimized, and the overall efficiency is slightly higher than that of an address transfer call.
3. execution efficiency. The execution efficiency here refers to the execution efficiency in the called function. When a value is called, the value is transferred to the function body. After a temporary object is generated, all execution tasks are executed in direct addressing mode, in most cases, pointers and references are executed in indirect addressing mode, so the actual execution efficiency is lower than that of value passing calls. If the function body frequently performs operations on the variables passed by parameters, in most cases, the passing of referenced parameters causes significant execution efficiency loss.
Based on the two situations, the specific execution efficiency should be combined with the actual situation. It is more appropriate to select the appropriate situation by comparing the sum of resource consumption in the transfer process and execution function body consumption. In contrast to pointer transfer, the efficiency of reference transfer is never lower than that of pointer transfer. In this sense, when passing parameters in C ++, the reference transmission instead of the pointer is preferred.
4. In terms of type security. Value Transfer and reference transfer both perform a strong type check during parameter transfer, while the type check for pointer transfer is weak. In particular, if the parameter is declared as void *, then there is basically no type check, as long as it is a pointer, the compiler will think it is legal, so this creates a chance to generate a BUG, so that the robustness of the program is slightly worse, if not necessary, it is best to use value transfer and reference transfer without passing pointers to make better use of the compiler type check, so that we have fewer error opportunities to increase code robustness.
Here is a special case: For polymorphism, if the form parameter is a parent class and the real parameter is a subclass, when passing the value, when a temporary object is constructed, only the part of the parent class is constructed. It is a pure parent class object, instead of any specific part of the Child class, because there is a virtual destructor, note that there is no virtual constructor. This cannot be implemented if you want to obtain the unique behavior of some sub-classes by calling the virtual function in the called function.
5. Check the parameters. A robust function always checks the passed parameters to ensure the legitimacy of the input data, so as to prevent data corruption and better control the program running in the desired direction, in this case, using a value transfer is much safer than using a pointer to pass, because you cannot pass a non-existent value to a value parameter or reference parameter, but using a pointer is possible, it is likely that an Invalid Address is sent (no initialization, pointer to the deleted object, etc ). Therefore, using value transfer and reference transfer will make your code more robust. The simplest principle is to check whether the passed data type is built-in, value transmission is preferred for internal data types. For custom data types, especially large objects, use references for transmission.
6. flexibility. No doubt, pointers are the most flexible, because pointers can not only pass a specific type of object like value passing and reference passing, but also pass null pointers without passing any objects. This advantage of pointer makes it useful. For example, you can pass a pointer to the time () function in the standard library and fill the time value to the specified address, you can also pass a null pointer as long as the return value is returned.
Based on the above several transfer methods, the list is as follows, so that you can select an appropriate transfer method in different situations. We divide various indicators into three levels: High, Medium, and low. The two methods at the same level are equivalent.
Transfer Mode |
Function |
Transfer Efficiency Embedded/custom |
Execution efficiency |
Type Security |
Parameter check |
Flexibility |
Multithreading |
Global Variables |
High |
High/High |
High |
High |
High |
Low |
Low |
Value |
Low |
High/low |
High |
Medium |
High |
Medium |
High |
Pointer |
High |
Low/Medium |
Low |
Low |
Low |
High |
Medium |
Reference |
High |
Medium/high |
Medium |
High |
High |
Medium |
Medium |
The advantages and disadvantages of the four parameter transfer methods are discussed above. Next we will discuss some common useful technologies in the parameter transfer process.
1. the const keyword. When your parameter is an input parameter, you do not want your input parameter to be modified. Otherwise, a logical error may occur. You can add the const keyword before the parameter when declaring the function, to prevent accidental modification of function input during implementation, programmers who use your code can also tell them that this parameter is input, and parameters without the const keyword may also be output. For example, strlen, you can declare
Int strlen (char * str );
There are certainly no functional issues, but you want to tell the person using this function that the str parameter is an input parameter and the data it points to cannot be modified, which is what they expect, no one will always want to ask someone to pay him a few yuan, and there will be 10 yuan in 100 yuan, or the real money will become fake money. They want a guarantee, this function will not destroy any of your data. The declaration can be reassuring as follows:
Int strlen (const char * str );
Can I add a limit to str itself? If I change the address to the correct number, isn't the result wrong? You have to give people some freedom, as long as it helps you count the money, why do you mind how he counts? As long as you do not damage your money, it will be OK. If you give str a limit, there will be problems. According to the above statement, you can achieve this:
Int strlen (const char * str)
{Int cnt;
If (! Str) return 0;
Cnt = 0;
While (* (str ++ )){
++ Cnt;
}
Return cnt;
}
However, if you want to change the statement
Int strlen (const char * const str );
The above functions cannot be run, and only other implementation methods can be used, but this is not necessary. As long as we protect our money, if the number is incorrect, next time I will not let it count, and then change to another person.
For a member function, if we want to show the client code that a member function will not modify the value of this object, it will only read some content, or add a const in the function declaration.
Clas Person
{......
Public:
Unsigned char Age (void) const; // you can rest assured that this function will not modify m_age.
Private:
Unsigned char m_age; // I think this type is long enough. If you don't want to change it, you can change it to unsigned long.
}
2. default value. I personally think that adding a default value to a parameter is very convenient and easy to use. In this way, you can define a function with several parameters and then give some default values to those parameters that are not commonly used, if the customer code thinks that the default values are exactly what they want, it is very convenient to call the function by filling in some necessary real parameters, which saves the trouble of overloading several functions. However, I don't understand why c # removed this feature, probably for security, so that it is required to assign real parameters to the function each time a function is called. Therefore, you must note that this is a double-edged sword. If you want to use a knife to fight with your opponent, you may be hurt.
3. Parameter order. When the same function name has different parameters, if there are the same parameters, try to put the parameters in the same location to facilitate client code.