High quality C/C ++ programming (6)

Source: Internet
Author: User

Chapter 2 Function DesignFunctions are the basic functional units of C ++/C Programs. Their importance is self-evident. The minor disadvantage of function design can easily lead to incorrect use of the function. Therefore, it is not enough to make the function correctly. This chapter focuses on the interface design and internal implementation rules of functions. Two elements of a function interface are parameters and return values. In C, function parameters and return values are transmitted in two ways: pass by value and pass by pointer ). Pass by reference is added to the C ++ language ). Because the nature of reference transfer is like pointer transfer, but the usage is like value transfer, beginners are often confused and prone to confusion. Please read section 6.6 "Comparison of reference and pointer" first ". 6.1 parameter rulesL Rule 6-1-1 ]Parameters must be completely written. Do not worry about writing only the parameter type and omitting the parameter name. If the function has no parameters, use void. Example: void setvalue (INT width, int height); // a good style void setvalue (INT, INT); // bad style float getvalue (void ); // good style float getvalue (); // bad style l Rule 6-1-2 ]The parameter name should be appropriate and the order should be reasonable. For example, compile the stringcopy function, which has two parameters. If the parameter names start with str1 and str2, such as void stringcopy (char * str1, char * str2), it is difficult to figure out whether str1 is copied to str2, it's just the opposite. The parameter names can be more meaningful, such as strsource and strdestination. In this way, we can see that strsource should be copied to strdestination. There is another question: Which of the two parameters should be the first one after? The order of parameters must follow the programmer's habits. Generally, the target parameter should be placed first, and the source parameter should be placed later. If the function is declared as void stringcopy (char * strsource, char * strdestination), others may write it as follows without thinking: Char STR [20]; stringcopy (STR, "Hello World"); // The Order of parameters is reversed. Rule 6-1-3 ]If the parameter is a pointer and only used for input, you should add const before the type to prevent the pointer from being accidentally modified in the function body. Example: void stringcopy (char * strdestination, const char * strsource); L Rule 6-1-4 ]If the input parameter is passed as a value, you should use the "const &" method to transfer the object. This saves the construction and analysis processes of the temporary object and improves the efficiency. 2. [Recommended 6-1-1 ]Avoid too many parameters in a function, and set the number of parameters to less than 5. If there are too many parameters, the parameter type or sequence may be incorrect during use. 2. [Recommended 6-1-2 ]Do not use parameters with uncertain types or numbers. The C standard library function printf is a typical example of using uncertain parameters. Its prototype is int printf (const chat * Format [, argument]…). This type of function loses strict type security checks during compilation. 6.2 return value rulesL Rule 6-2-1 ]Do not omit the type of the returned value. In C language, all functions without type descriptions are automatically processed by integer type. This method is easy to misunderstand as the void type. The C ++ language has strict type security checks and does not allow the above situations. Since C ++ programs can call C functions, to avoid confusion, it is required that any c ++/C function must have a type. If the function does not return a value, the void type should be declared. L Rule 6-2-2 ]The function name and return value type cannot conflict in semantics. A typical violation of this rule is the c Standard library function getchar. For example: Char C; C = getchar (); If (C = EOF )... According to the getchar name, it is natural to declare the variable C as the char type. Unfortunately, getchar is not a char type but an int type. Its prototype is int getchar (void). Since C is a char type, the value range is [-128,127]. if the macro EOF value is out of the char value range, the IF statement will always fail. This "dangerous" is expected! The fault in this example is not caused by the user. The getchar function misleads the user. L Rule 6-2-3 ]Do not mix the normal value with the error flag to return the result. The normal value is obtained using the output parameter, and the error mark is returned using the return statement. In the previous example, why should the C standard library function designers declare getchar as a confusing int type? Will he be so stupid? Normally, getchar returns a single character. However, if getchar encounters a file end mark or a read error, it must return an EOF flag. To distinguish it from a normal character, we have to define the EOF as a negative number (usually negative 1 ). Therefore, the getchar function is of the int type. In our practical work, we often encounter the above difficult problems. To avoid misunderstanding, we should separate the normal value from the error mark. That is, the normal value is obtained using the output parameter, and the error mark is returned using the return statement. The getchar function can be rewritten to bool getchar (char * C). Although gechar is more flexible than getchar, such as putchar (getchar (), if getchar is used incorrectly, what is its flexibility? 2. [Recommended 6-2-1 ]Sometimes a function does not need to return values, but to increase flexibility, such as supporting chained expressions, you can add return values. For example, the prototype of the string copy function strcpy is char * strcpy (char * strdest, const char * strsrc). The strcpy function copies strsrc to the output parameter strdest, And the return value of the function is strdest. In this case, the following flexibility can be obtained: Char STR [20]; int length = strlen (strcpy (STR, "Hello World"); 2 [Recommended 6-2-2 ]If the return value of a function is an object, in some cases, replacing "value transfer" with "reference transfer" can improve the efficiency. In some cases, you can only use "value transfer" instead of "reference transfer". Otherwise, an error occurs. Example: Class string {... // Value assignment function string & operate = (const string & other); // addition function. If there is no friend modifier, only one right parameter friend string operate + (const string & S1, const string & S2); Private: char * m_data;} The Implementation of the string value assignment function operate = is as follows: string & string: operate = (const string & other) {If (this = & Other) return * This; Delete m_data; m_data = new char [strlen (Other. data) + 1]; strcpy (m_data, other. data); return * This; // The returned result is a reference to * This, without the need to copy the process }. Function. The String object should be returned in the form of "reference transfer. If the "value transfer" method is used, although the function is still correct, but because the return statement needs to copy * this to the external storage unit that saves the returned value, unnecessary overhead is added, this reduces the efficiency of the value assignment function. Example: string a, B, c ;... A = B; // If "value transfer" is used, a * This copy a = B = C; // If "value transfer" is used ", the addition function operate + that generates two * This copies of string is implemented as follows: String operate + (const string & S1, const string & S2) {string temp; Delete temp. data; // temp. data is a string containing '\ 0' only. data = new char [strlen (s1.data) + strlen (s2.data) + 1]; strcpy (temp. data, s1.data); strcat (temp. data, s2.data); Return temp;} for addition functions, the string object should be returned by "passing values. If "reference transfer" is used, the return value of the function is a "Reference" pointing to the local object temp ". Because temp is automatically destroyed at the end of the function, the returned "Reference" is invalid. For example, c = a + B. At this time, A + B does not return the expected value, and C does not get anything, causing hidden risks. 6.3 internal implementation rules of functionsDifferent functions have different internal implementations, and it seems impossible to reach an agreement on internal implementations. However, based on experience, we can strictly control the "ENTRANCE" and "exit" of the function body to improve the function quality. L Rule 6-3-1 ]Check the parameter validity at the "ENTRANCE" of the function body. Many program errors are caused by Invalid parameters. We should fully understand and correctly use assert to prevent such errors. For details, see section 6.5 "Use assertions ". L Rule 6-3-2 ]Check the correctness and efficiency of the Return Statement at the "exit" of the function body. If a function returns a value, the "exit" of the function is the return statement. We should not underestimate the return statement. If the return statement is not well written, the function either fails or is inefficient. Note: (1) the return statement cannot return "Pointer" or "Reference" pointing to "stack memory" because the function body is automatically destroyed when it ends. For example, char * func (void) {char STR [] = "Hello World"; // STR memory is located on the stack... Return STR; // will cause an error} (2) Determine whether the returned result is "value", "Pointer", or "Reference ". (3) If the return value of a function is an object, the efficiency of the return statement should be considered. For example, return string (S1 + S2); this is the syntax of a temporary object, indicating "Creating a temporary object and returning it ". Do not think it is equivalent to "first create a local object temp and return its results", such as string temp (S1 + S2); Return temp; otherwise, three things will happen in the above Code. First, the temp object is created and initialized at the same time. Then, the copy constructor copies temp to the external storage unit that stores the returned values. Finally, temp is destroyed at the end of the function (the Destructor is called ). However, the process of "Creating a temporary object and returning it" is different. The Compiler directly creates and initializes the temporary object in an external storage unit, saving the copy and destructor fees, improved efficiency. Similarly, do not create a temporary variable and return it as int temp = x + y; return temp; because the internal data types such as int, float, and double do not have constructor and destructor, although the "Syntax of temporary variables" does not increase much efficiency, the program is more concise and easy to read. 6.4 other suggestions2. [Recommended 6-4-1 ]A single function is required. Do not design a multi-purpose function. 2. [Recommended 6-4-2 ]The size of the function body should be small and should be controlled within 50 lines of code as much as possible. 2. [6-4-3 is recommended. ]Do not use the "Memory" function. The same input should generate the same output. For a function with the "Memory" function, its behavior may be unpredictable, because its behavior may depend on a certain "memory state ". Such functions are hard to understand and are not conducive to testing and maintenance. In C/C ++, the static local variable of the function is the memory of the function. We recommend that you use less static local variables unless necessary. 2. [Recommended 6-4-4 ]Check not only the validity of input parameters, but also the validity of variables entering the function body through other channels, such as global variables and file handles. 2. [Recommended 6-4-5 ]The returned values for error handling must be clear, so that the user cannot easily ignore or misunderstand the error. 6.5 Use assertionsPrograms are generally divided into debug and release versions. The debug version is used for internal debugging, And the release version is released to users. Assert is a macro that only works in the debug version. It is used to check the situation where "no" occurs. Example 6-5 is a memory replication function. During the running process, if the assert parameter is false, the program will stop (generally, a dialog prompt will appear, indicating where the assert is triggered ).
Void * memcpy (void * pvto, const void * pvfrom, size_t size) {assert (pvto! = NULL) & (pvfrom! = NULL); // use the asserted byte * pbto = (byte *) pvto; // prevent the pvto address from changing byte * pbfrom = (byte *) pvfrom; // prevent changing the pvfrom address while (size --> 0) * pbto ++ = * pbfrom ++; return pvto ;}
Example 6-5 copying a non-overlapping memory block assert is not a hasty macro. In order not to cause any difference between the debug and release versions of the program, assert should not produce any side effects. So assert is not a function, but a macro. Programmers can regard assert as a harmless testing method that can be safely used in any system status. If the program Assert The specified assert is terminated. But the caller has an error. Assert This helps us find the cause of the error.There are few more frustrating things than the assertions that trace the program, but do not know the role of the assertions. You have made a lot of time, not to exclude errors, but to find out what the error is. Sometimes, programmers occasionally design wrong assertions. Therefore, if you do not know what the assertions check, it is difficult to determine whether the errors appear in the program or in the assertions. Fortunately, this problem is well solved by adding clear notes. This is obvious, but few programmers do this. This is like a person in the forest, seeing a "dangerous" big sign on the tree. But what is the danger? Will the tree fall? Is there a waste well? Is there a beast? Unless you tell people what danger is, this warning board cannot play a positive and effective role. Incomprehensible assertions are often ignored or even deleted by programmers. [Maguire, p8-p30] l Rule 6-5-1 ]Use assertions to capture situations that should not occur. Do not confuse the differences between illegal and wrong situations. The latter must exist and be handled. L Rule 6-5-2 ] InAt the entry of the function, use assertions to check the validity (validity) of the parameter ). L [Recommended 6-5-1 ]When writing a function, you need to repeat it and ask yourself: "What assumptions do I plan to make ?" Once the assumptions are determined, we need to use assertions to check the assumptions. L [Recommended 6-5-2 ]In general, textbooks encourage programmers to design error prevention, but remember that such a programming style may conceal errors. When an error prevention design occurs, if the "impossible to happen" event does occur, Use assertions to trigger an alarm. 6.6 Comparison Between Reference and pointerReferences are a concept in C ++. Beginners can easily confuse references with pointers. In the following program, n is a reference of M, and M is a referent ). Int m; Int & n = m; n is equivalent to the alias (nickname) of M. Any operation on N is an operation on M. For example, someone named Wang xiaomao, whose nickname is "San Mao ". The reason for saying "three hairs" is actually to say three things to Wang xiaomao. Therefore, n is neither a copy of M nor a pointer to M. In fact, n is m itself. Some rules for reference are as follows: (1) When a reference is created, it must be initialized (the pointer can be initialized at any time ). (2) there cannot be a null reference, and the reference must be associated with a valid storage unit (the pointer can be null ). (3) once the reference is initialized, the reference relationship cannot be changed (the pointer can change the object at any time ). In the following example, K is initialized as a reference of I. Statement K = J cannot be changed to a reference of J, but the value of K is changed to 6. Since K is an I reference, the I value is also changed to 6. Int I = 5; Int J = 6; Int & K = I; k = J; // The values of K and I are changed to 6; the above program looks like playing a text game and does not reflect the value of reference. The main function of the reference is to pass the parameters and return values of the function. In C ++, function parameters and return values are transmitted in three ways: value transfer, pointer transfer, and reference transfer. The following is a sample program for "value transfer. Since X in the func1 function is a copy of the external variable N, changing the value of X does not affect N, so the value of N is still 0. Void func1 (int x) {x = x + 10 ;}... Int n = 0; func1 (n); cout <"n =" <n <Endl; // below n = 0 is a sample program for "pointer passing. Since X in the func2 function is a pointer to the external variable N, changing the content of this pointer will change the value of N, so the value of N is 10. Void func2 (int * X) {(* X) = (* x) + 10 ;}... Int n = 0; func2 (& N); cout <"n =" <n <Endl; // n = 10 The following is a sample program for "reference transfer. Since X in the func3 function is a reference of the external variable n, x and n are the same thing, changing X is equal to changing N, so the value of N is 10. Void func3 (Int & X) {x = x + 10 ;}... Int n = 0; func3 (n); cout <"n =" <n <Endl; // n = 10 compare the above three sample programs, we will find that the nature of "reference transfer" is like "pointer transfer", and the writing method is like "value transfer ". In fact, all the things that can be done by "reference" can also be done by "pointer". Why do we need to "reference" this? The answer is "Use appropriate tools for proper work ". Pointers can operate on the memory without any constraints. Although pointers are powerful, they are very dangerous. Like a knife, which can be used to cut trees, cut paper, manicure, and cut hair. Who dares to use it like this? If you only need to borrow the "alias" of an object, use "Reference" instead of "Pointer" to avoid exceptions. For example, a person needs a proof that the seal was originally printed on the document. If he handed over the key to the seal, he would have obtained the right he did not have.

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.