High-quality C ++/C Programming Guide-Chapter 1 function design

Source: Internet
Author: User

Chapter 2 Function Design

The function is C ++/C.ProgramIts 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 rules
[Rule 6-1-1] parameters must be completely written. Do not worry about writing only the parameter type, but omit the parameter name. If the function has no parameters, use void.
For example:
Void setvalue (INT width, int height); // good style
Void setvalue (INT, INT); // bad style
Float getvalue (void); // good style
Float getvalue (); // bad style
 
[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 are str1 and str2, for example, void stringcopy (char * str1, char * str2 );

It is difficult to figure out whether to copy str1 to str2.
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 you declare a function:
Void stringcopy (char * strsource, char * strdestination );
When using it, others may write the following form without thinking about it:
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, add const before the type to prevent the pointer from being accidentally modified in the function body.
For example:
Void stringcopy (char * strdestination, const char * strsource );

[Rule 6-1-4] If an input parameter transmits an object as a value, you should use the "const &" method to transfer the object. In this way, the construction and analysis process of the temporary object can be omitted, to improve efficiency.

[6-1-1] avoid having too many parameters in the function. The number of parameters should be limited to five. If there are too many parameters, the parameter type or sequence may be incorrect during use.

[6-1-2] Do not use parameters with uncertain types and 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 rules
[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.

[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 as follows:
Int getchar (void );

Because C is of the char type and 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 kind of "dangerous" is generally expected! The fault in this example is not caused by the user. The getchar function misleads the user.

[Rule 6-2-3] Do not mix the normal value with the error mark 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 function getchar can be rewritten to bool getchar (char * C );
Although gechar is more flexible than getchar, such as putchar (getchar (), what is its flexibility if getchar is used incorrectly?

[6-2-1] Sometimes the 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:
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 achieved:
Char STR [20];
Int length = strlen (strcpy (STR, "Hello World "));

[6-2-2] If the return value of a function is an object, replacing "value transfer" with "reference transfer" may improve the efficiency. In some cases, you can only use "value transfer" instead of "reference transfer". Otherwise, an error occurs.
For example:
Class string
{...
// Value assignment function
String & operate = (const string & other );
// Add function. If there is no friend modifier, only one parameter on the right is allowed.
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 of * This, without the need to copy the process.
}
For the value assignment function, the string object should be returned using the "reference transfer" method. 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. For example:
String A, B, C;
...
A = B; // If "value transfer" is used, a * This copy will be generated.
A = B = C; // If "value transfer" is used, two * This copies will be generated.

The implementation of the string addition function operate + is as follows:
String operate + (const string & S1, const string & S2)
{
String temp;
Delete temp. Data; // temp. Data is a string containing ''only.
Temp. 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 using the "value transfer" method. 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 functions
Different 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.

[Rule 6-3-1] checks 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.
Error. For details, see section 6.5 "Use assertions ".

[Rule 6-3-2] checks 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"; // the memory of STR is on the stack.
...
Return STR; // 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
String temp (S1 + S2 );
Return temp;
Otherwise Code Three things will happen. 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 write the return int (x + y); // create a temporary variable and return it
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 suggestions
[6-4-1] The function should have a single function. Do not design a multi-purpose function.
[6-4-2] the size of the function body should be small and should be controlled within 50 lines of code as far as possible.
[6-4-3] 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.

[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.

[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 assertions
Programs 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 assertions
Byte * pbto = (byte *) pvto; // prevents the pvto address from being changed
Byte * pbfrom = (byte *) pvfrom; // prevents the pvfrom address from being changed
While (size --> 0)
* Pbto ++ = * pbfrom ++;
Return pvto;
}
Example 6-5 Copy non-overlapping memory blocks

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 terminates at assert, it does not mean that the function containing this assert has an error, but the caller has an error. Assert can help 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. Just add a clear comment. This is obvious

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.