High Quality C ++C Programming Guide (Senior Characteristics of Chapter 8 C ++ Function)

zhaozj2021-02-17  58

Chapter 8 Advanced features of C functions

Compared to the function of the C language, C increases overloaded, inline, constel, 40 new mechanisms. The overload and inline mechanism can be used for global functions can also be used for members functions for classes, and the const and virtual mechanisms are only used for class's member functions.

The overload and inline are certainly adopted by the C language, but it is not possible to be abused as a free lunch. This chapter will explore the advantages and limitations of overload and inline, indicating what circumstances should be adopted, should not be used, and be vigilant.

8.1 Concept of function overload

8.1.1 Overloaded origin

In the natural language, a word can have many different meanings, ie the word is overloaded. It can be judged from the context to which meaning is. "The overload of the word" can make the language more concise. For example, "Eating" has a wide range of people, and people don't have to say clearly what to eat every time. Don't be a hole in the hole, saying four kinds of writing.

In the C program, several functions similar to the semantic and functionality can be represented by the same name, that is, the function is overloaded. This makes it easy to remember, improve the ease of use of functions, which is a reason for the use of overloading mechanisms in C languages. For example, the functions in Examples 8-1-1 Eatbeef, Eatfish, Eatchicke can be represented by the same function name EAT, and different types of parameters.

Void Eatbeef (...); // can be changed to Void Eat (beef ...);

Void Eatfish (...); // can be changed to Void Eat (Fish ...);

Void eatchicken (...); // can be changed to Void Eat (Chicken ...);

Example 8-1-1 Overload function EAT

Another reason for the use of overload mechanisms in the C language is that the constructor of the class requires the overload mechanism. Because C specifies the constructor and the same name (see Chapter 9), the constructor can only have a name. What should I do if I want to create an object with several different ways? There is no choice, you can only be implemented with an overload mechanism. So the class can have a plurality of constructors of the same name.

8.1.2 How is overloaded?

Several of the same name's overload function is still different, how is it distinguished? We naturally think of two elements of the function interface: parameters and return values.

If the parameters of the same name function are different (including type, the order is different), it is easy to distinguish between different functions.

If the same name function is simply different, sometimes it can be distinguished, sometimes it is not possible. E.g:

Void function (void);

INT function (void);

The above two functions, the first no return value, the second return value is the Int type. If this is called the function:

INT x = function ();

It can be judged that Function is the second function. The problem is in the C / C program, we can ignore the return value of the function. In this case, both the compiler and programmers don't know which Function function is called.

Therefore, you can only distinguish the overload function with different parameters and cannot rely on the return value type. The compiler produces different internal identifiers to each overload function according to the parameters. For example, the compiler is the three EAT functions generated in Examples 8-1-1 generated image _eat_beef, _eat_fish, _eat_chicken, and the internal identifier (different compilers) can produce internal identifiers of different styles).

What should I do if the C program wants to call the C function that has been compiled?

Suppose a declaration of a C function is as follows:

Void foo (int x, int y);

This function is compiled by the C compiler in the name of the library as _foo, and the C compiler produces a name like _foo_int_int to support function overload and type secure connection. Due to the different names, the C program cannot directly call C functions. C provides a C-connection exchange specifying symbol EXTERN "C" to solve this problem. For example: Extern "C"

{

Void foo (int x, int y);

... // Other functions

}

Written

EXTERN "C"

{

#include "myHeader.h"

... // Other C header files

}

This tells the C compiler, the function foo is a C connection, you should go to the library to find name _foo instead of find _foo_int_int. The C compiler developer has made Extern "C" on the header file of the C standard library, so we can directly reference these header files directly with #include.

Note that it is not the same as the name of the two functions. The global function and the member function of the class are not overloaded because the function of the function is different. E.g:

Void Print (...); // Global function

Class A

{...

Void Print (...); // member function

}

Regardless of whether the parameters of the two print functions are different. If a member function of the class wants to call the global function Print, in order to distinguish between the member function Print, the global function should be added to the '::' flag. Such as

:: Print (...); // means print is a global function rather than member functions

8.1.3 Beware of implicit type conversion causes the overload function to generate an unity

In Examples 8-1-3, the parameters of the first OUTPUT function are int types, and the parameters of the second OUTPUT function are Float types. Since the number itself does not have type, the number conversion will be automatically performed when the number is used as a parameter (called implicit type conversion). Statement OUTPUT (0.5) will generate compilation errors because the compiler does not know the parameters of converting 0.5 to int or a float type. Implicit type conversion can simplify the writing of the program in many places, but may also leave hidden dangers.

# include

Void Output (int x); // function declaration

void output (float x); // function declaration

Void Output (INT X)

{

Cout << "Output Int" << x << endl;

}

Void Output (Float X)

{

Cout << "Output float" << x << endl;

}

Void main (void)

{

INT x = 1;

Float y = 1.0;

Output (x); // Output INT 1

Output (y); // output float 1

Output (1); // Output INT 1

// Output (0.5); // error! Ambiguous Call, because of the automatic type conversion

Output (int (0.5)); // Output INT 0

Output (float (0.5)); // output float 0.5

}

Example 8-1-3 Implicit Type Conversion causes the overload function to generate an amphony

8.2 Overload, coverage and hidden

The overload of the member function, override and hidden are easy to confuse, C programmers must figure out the concept, otherwise the error will prevent it.

8.2.1 overloading and coverage

Member function is overloaded:

(1) The same range (in the same class); (2) The function name is the same;

(3) Different parameters;

(4) Virtual keywords can be available.

The overlay is a basic class function that is specifically class function, feature:

(1) Different scope (located in derived class and base class);

(2) The function name is the same;

(3) The parameter is the same;

(4) The base class function must have a Virtual keyword.

Example 8-2-1, the function base :: f (int) is overloaded with Base :: F (FLOAT), while Base :: g (void) is overwritten by Derived :: G (Void).

#include

Class Base

{

PUBLIC:

Void f (int x) {cout << "Base :: f (int) << x << endl;}

Void f (float x) {cout << "Base :: f (float) << x << endl;}

Virtual void g (void) {cout << "Base :: g (void) << endl;}

}

Class Derived: Public Base

{

PUBLIC:

Virtual Void G (void) {cout << "Derived :: g (void) << endl;}

}

Void main (void)

{

Derived D;

Base * Pb = & D;

Pb-> f (42); // Base :: f (int) 42

Pb-> f (3.14f); // Base :: F (Float) 3.14

Pb-> g (); // derived :: g (void)

}

Example 8-2-1 Overload and overwriting of member functions

8.2.2 Confusing hidden rules

It is not difficult to distinguish between heavy duty and coverage, but C hidden rules have suddenly increased problem complexity. Here "hidden" is the function of the function of the born class to block the base class function with its same name, the rules are as follows:

(1) If the derived class function is the same name, the parameters are different. At this point, the function of the base class will be hidden, whether there is a Virtual keyword, and not to confuse the overload.

(2) If the derived class function is the same name, and the parameters are the same, the base class function does not have a Virtual keyword. At this point, the function of the base class is hidden (notes confusing with overlay).

Example Program 8-2-2 (a):

(1) Function Derived :: F (FLOAT) covers Base :: F (Float).

(2) Function Derived :: G (int) Hide Base :: g (float) instead of overload.

(3) Function Derived :: h (float) Hide Base :: h (float) instead of overwriting.

#include

Class Base

{

PUBLIC:

Virtual void f (float x) {cout << "Base :: f (float) << x << endl;} void g (float x) {cout <<" Base :: g (float) << x << Endl;}

Void h (float x) {cout << "Base :: h (float) << x << endl;}

}

Class Derived: Public Base

{

PUBLIC:

Virtual void f (float x) {cout << "Derived :: f (float) << x << endl;}

Void g (int x) {cout << "Derived :: g (int) << x << endl;}

Void h (float x) {cout << "Derived :: h (float) << x << endl;}

}

Example 8-2-2 (a) Overload, coverage and hidden

According to the author, many C programmers did not realize that there is "hidden". Because of understanding is not deep enough, "hidden" can be said that God is not, often generates confusing results.

In Examples 8-2-2 (b), BP and DP points to the same address. It should be the same, which should be the same, which is not the case.

Void main (void)

{

Derived D;

Base * Pb = & D;

Derived * pd = & D;

// Good: Behavior Depends Solely on Type of the Object

Pb-> f (3.14f); // derived :: f (float) 3.14

PD-> f (3.14f); // derived :: f (float) 3.14

// Bad: Behavior Depends on Type of The Pointer

Pb-> g (3.14f); // Base :: g (float) 3.14

PD-> g (3.14f); // derived :: g (int) 3 (surprise!)

// Bad: Behavior Depends on Type of The Pointer

Pb-> h (3.14f); // Base :: h (float) 3.14 (surprise!)

PD-> h (3.14f); // Derived :: h (float) 3.14

}

Example 8-2-2 (b) Overload, overlay, and hidden comparison

8.2.3 get rid of hidden

The hidden rules have caused a lot of trouble. Example 8-2-3 Program, the original sentence PD-> f (10) is the original meaning to call the function base :: f (int), but Base :: f (int) is hidden by derived :: f (char *) . Since the number 10 cannot be implicitly converted to a string, it is wrong when compiling.

Class Base

{

PUBLIC:

Void f (int X);

}

Class Derived: Public Base

{

PUBLIC:

Void F (Char * STR);

}

Void test (void)

{

Derived * pd = new deerid;

PD-> f (10); // error

}

Example 8-2-3 Error due to hidden

From example 8-2-3, hidden rules seem to be stupid. But there are at least two reasons for hidden rules: U-write statements PD-> F (10) may really want to call the Derived :: F (Char *) function, but he misuses the parameters wrong. With hidden rules, the compiler can clearly point out errors, which is not necessarily good. Otherwise, the compiler will quietly pass the wrong, and the programmer will hardly find this error, and the root will be found.

u If the class derived has multiple base classes (multiple inheritance), sometimes it is clear which base classes define the function f. If there is no hidden rule, then PD-> F (10) may call an unexpected base class function F. Although the hidden rules do not seem to make reasonable, it can indeed destroy these accidents.

Example 8-2-3, if the statement PD-> f (10) must call the function base :: f (int), then modify the class DeriveD to the following.

Class Derived: Public Base

{

PUBLIC:

Void F (Char * STR);

Void f (int x) {base :: f (x);

}

8.3 Default value for parameters

There are some parameters that are the same when the function is called, and writing such a statement will make people bored. The default value of the C language uses the writing to make the writing simple (when compiling, the default is automatically inserted by the compiler).

Use rules for the use of parameters defaults:

l [Rules 8-3-1] Parameter default values ​​can only appear in a declaration of functions, and cannot appear in the defined body.

E.g:

Void foo (int x = 0, int y = 0); / / correct, default, in the declaration of functions

Void foo (int x = 0, int y = 0) // error, the default value appears in the defined body of the function

{

...

}

Why is this this? I think there are two reasons: First, the implementation of the function (defined) has not been related to the default value of the parameter, so it is not necessary to make the default value appear in the defined body of the function. Second, the default value of the parameters may be changed, apparently modifying the declaration of the function is convenient than the definition of the modification function.

l [Rules 8-3-2] If the function has multiple parameters, the parameters can only be deficed from the back forward, otherwise the function call statement is blamed.

The correct example is as follows:

Void foo (int x, int y = 0, int z = 0);

The error is as follows:

Void foo (int x = 0, int y, int z = 0);

It is to be noted that the default value of the parameters does not give a function of a function, but it is simply written. It may increase the ease of use, but it may also reduce the understandability of the function. So we can only use the default values ​​of parameters to prevent improper use of improper use. In Examples 8-3-2, the default value of the unreasonable use of the parameters will result in an empirieity of the overload function OUTPUT.

#include

Void Output (int X);

Void Output (int X, float y = 0.0);

Void Output (INT X)

{

Cout << "Output Int" << x << endl;

}

Void Output (int x, float y)

{

COUT << "Output Int" << x << "and float" << Y << ENDL;

}

Void main (void) {

INT x = 1;

Float y = 0.5;

// Output (x); // error! Ambiguous Call

Output (x, y); // Output Int 1 and float 0.5

}

The default value of the parameter 8-3-2 parameters will result in an emphasis function

8.4 operator overload

8.4.1 concept

In the C language, the operator can be represented by the keyword operator to represent the function, called an operator overload. For example, two plural addition functions:

Complex Add (Const Complex & A, Const Complex & B);

You can use an operator overload to indicate:

Complex Operator (Const Complex & A, Const Complex & B);

The difference between the operator and the normal function is in the call: for normal functions, the parameters appear in parentheses; for operators, parameters appear on their left and right. E.g

Complex A, B, C;

...

C = add (a, b); // with a normal function

C = a b; // with operator

If the operator is overloaded as a global function, only one arithmetic operator is called a yuan operator, and there are two arguments called a binary operator.

If the operator is overloaded as a member function, then a yuan operator does not have a parameter, and the binary operator has only one right parameter because the object itself has a left parameter.

From the grammatical speaking, the operator can be defined as a global function or as a member function. The literature [Murray, P44-P47] has made more elaborated on this issue and summarizes the rules of Table 8-4-1.

Operator

rule

All one yuan operator

Recommended to overreload as a member function

= () [] ->

Can only be overloaded as a member function

= - = / = * = & = | = ~ =% = >> = << =

Recommended to overreload as a member function

All other operators

Recommended overload is a global function

Table 8-4-1 Ondup rules for operators

Since the C language support function is overloaded, the operator can be used as a function, and the C language will not work. We have to treat the operator overloaded by usual heart:

(1) Don't be too worried that you will not use it, its essence is still a function that the programmer is familiar.

(2) Don't use too much enthusiasm, if it can't make the code more easy to read, then don't use it, otherwise you will find trouble.

8.4.2 Operators that cannot be overloaded

In the C operator collection, some operators are not allowed to be overloaded. This limitation is for security considerations to prevent errors and confusion.

(1) Operators of C internal data types such as INT, FLOAT, etc.) cannot be changed.

(2) You cannot overload '.', Because '.' It makes sense to any member in the class, has become standard usage.

(3) You cannot overload symbols in the current C operator collection, such as #, @, $, etc. There are two reasons, one is difficult to understand, and the other is difficult to determine the priority.

(4) When the existing operator is overloaded, the priority rule cannot be changed, otherwise it will cause confusion.

8.5 function inline

8.5.1 Dissiphasia with inline

The C language support function is inline, its purpose is to improve the performance efficiency (speed) of the function. In the C program, the execution efficiency can be improved by macro code. The macro code itself is not a function, but uses the icon function. Pre-regulators use the copy macro code instead of the function call, save the parameter stack, generate the CALL call of the assembly language, return to the parameters, execute Return, etc., thereby improving the speed. The maximum disadvantage of using macro code is easy to make mistakes, and the preprocessor is often unexpected when copying macro code. E.g

#define max (a, b) (a)> (b)? (a): (b)

Statement

Result = max (i, j) 2;

Explain the pre-processor

Result = (i)> (j)? (i): (j) 2;

Since the operator ' ' ratio operator ':' is high, the above statement does not equivalent to expectations.

Result = ((i)> (j)? (i): (j)) 2;

If the macro code is rewritten as

#define max (a, b) ((a)> (b)? (a): (b))

The errors caused by priority can be resolved. But even if you use the modified macro code, it is not very no loss, such as a statement.

Result = max (i , j);

Explain the pre-processor

Result = (i )> (j)? (i ): (j);

For C , there is another disadvantage to use the macro code: a private data member that cannot be operated.

Let us look at how C "function inline" works. For any end function, the compiler is placed in a function of the function (including the name, parameter type, and the return value type). If the compiler does not find an inner function error, the code of the function is also placed in a symbol table. When calling an inline function, the compiler first checks if the call is correct (for type security check, or automatic type conversion, of course, all functions). If it is correct, the code of the inline function will replace the function calls directly, so I save the overhead of the function call. This process is significantly different from pretreatment because the preprocessor cannot perform type security checks, or automatic type conversion. If the inline function is a member function, the address of the object (this) will be placed in a suitable place, which is also not possible.

The function inline mechanism of the C language has both the efficiency of macro code, but also the security of the information, and the data member of the class. So in the C program, you should use the inner function to replace all macro code, "assertion assert" is probably the only exception. Assert is a macro that works only on the Debug version, which is used to check the situation that "should not" happen. In order not to cause a difference between the Debug version and the Release version, Assert should not generate any side effects. If the assert is a function, since the function call causes the memory, the code changes, then there will be differences in the DEBUG version and the Release version. So Assert is not a function, but a macro. (See "Using Assessment" in Section 6.5))

8.5.2 Inline function programming style

Keyword Inline must be placed with a function definition to make the function be inline, and only the Inline is placed in front of the function declaration. The following style function foo cannot be an inline function:

Inline void foo (int x, int y); // Inline is only put together with the function declaration

Void foo (int x, int y)

{

...

}

And the following style function foo is an inline function: Void foo (int X, int y);

Inline void foo (int x, int y) // inline is set with functions

{

...

}

So, inline is a "keyword for implementation", not a "keyword used to declare". Generally, users can read the declarations of the function, but can not see the definition of the function. Although the inline keyword is added in front of the inner function in most textbooks, I think INLINE should not appear in the function of the function. Although this details do not affect the functionality of the function, it reflects a basic principle of high quality C / C programming style: declaration and definition can not be confused, users don't have to be necessary, should not know if the function needs inline.

Defining the member function in the class declaration will automatically be an inline function, for example

Class A

{

PUBLIC:

Void foo (int x, int y) {...} // automatically becomes inline function

}

Putting the definition of the member function in the class declaration, although it can bring convenience on writing, but not a good programming style, the above case should be changed:

// head File

Class A

{

PUBLIC:

Void foo (int x, int y);

}

// Define the file

Inline void a :: foo (int x, int y)

{

...

}

8.5.3 caution inline

Inline Can improve the performance efficiency of the function, why not define all functions into an intanic function?

If all functions are inline functions, do you have "inline" keywords?

The inline is at the expense of code expansion (copy), and only saves the overhead of the function call, thereby increasing the efficiency of the function. If the time of the function is executed, it will be less efficient than the overhead of the function call. On the other hand, each inline function is called to copy the code, which will increase the total amount of the total code amount to consume more memory space. The following cases should not be used in line:

(1) If the code in the function is relatively long, the use of inline will result in higher memory consumption.

(2) If a loop occurs in a function, the time to perform the code in the function is larger than the overhead of the function call.

The constructor and the destructive function of the class are easy to misunderstand the inline. To be aware of the constructor and the destructuring function, some behavior may hide, such as "sneak" executive function and description function of the base class or member object. So don't place the constructor and the definition of the destructor in the class declaration.

A good compiler will automatically cancel the inline that is not worthless according to the definition of the function (this further illustrates inline should not appear in the declaration of the function).

8.6 Some experience

Overload, inline, default parameters, implicit conversion in the C language show a lot of advantages, but behind these advantages have hidden hidden dangers. As people's diet, less food and overeatment are not advisable, it should be just right. We want to identify the new mechanism of C , which should be used in part. Although this will make us more expensive, there are some happy, but this is the art of programming.

转载请注明原文地址:https://www.9cbs.com/read-28723.html

New Post(0)