High quality C ++/C Programming Guide-advanced features of C ++ Functions

Source: Internet
Author: User

C ++FunctionAdvanced features

Compared with C functions, C ++ adds four new mechanisms: overloaded, inline, const, and virtual. The overload and inline mechanisms can be used for both global functions and class member functions. The const and virtual mechanisms are only used for class member functions.

Heavy Load and inline will certainly have their advantages to be adopted by the C ++ language, but it cannot be abused as a free lunch. This chapter will explore the advantages and limitations of heavy load and inline, and explain under what circumstances should be adopted, should not be used, and should be vigilant against mistakes.

8.1FunctionConcept of heavy load

8.1.1 origin of heavy load

In natural language, a word can have many different meanings, that is, it is overloaded. People can use context to determine the meaning of a word. "Word overload" can make the language more concise. For example, "eat" has a wide range of meanings, and people do not have to clarify what to eat each time. Do not peat Kong as you have already said that there are four ways to write the Z word.

In a C ++ program, several functions with similar semantics and functions can be represented by the same name, that is, function overloading. This makes it easier to remember and improves the ease of use of functions. This is one reason why C ++ uses the overload mechanism. For example, the functions eatbeef, eatfish, and eatchicken In the example 8-1-1 can be represented by the same function name eat and different types of parameters.

Void eatbeef (...); // You can change it to void eat (beef ...);

Void eatfish (...); // You can change it to void eat (fish ...);

Void eatchicken (...); // You can change it to void eat (Chicken ...);

Example 8-1-1 overload function eat

Another reason why C ++ uses the overload mechanism is that the class constructor needs to overload the mechanism. C ++ requires that the constructor and the class have the same name (see Chapter 9th). A constructor can have only one name. What should I do if I want to create an object using several different methods? There is no choice but to use the overload mechanism. Therefore, a class can have multiple constructors with the same name.

8.1.2 how is the overload implemented?

Several duplicate functions with the same name are still different. How are they differentiated? We naturally think of two elements of a function interface: parameters and return values.

If the parameters of functions with the same name are different (including different types and order), they are different functions.

If functions with the same name only have different return value types, they can be distinguished, but sometimes they cannot. For example:

Void function (void );

Int function (void );

The first function has no return value, and the second function has the int type. If you call a function like this:

Int x = function ();

Then we can determine that the function is the second function. The problem is that in C ++/C Programs, we can ignore the return value of the function. In this case, neither the compiler nor the programmer knows which function is called.

Therefore, you can only distinguish between overloaded functions by parameters rather than different return value types. The compiler generates different internal identifiers for each overload function based on parameters. For example, the compiler generates internal identifiers such as _ eat_beef, _ eat_fish, and _ eat_chicken for the three eat functions in Example 8-1-1 (different compilers may generate internal identifiers of different styles ).

What if a C ++ program needs to call a compiled C function?

Assume that the declaration of a C function is as follows:

Void Foo (int x, int y );

After the function is compiled by the C compiler, its name in the library is _ Foo, while the C ++ compiler generates names such as _ foo_int_int to support function overloading and secure type connection. C ++ programs cannot directly call C functions because their compiled names are different. C ++ provides a C connection to exchange the specified symbol extern "C" to solve this problem. For example:

Extern "C"

{

Void Foo (int x, int y );

... // Other functions

}

Or write it

Extern "C"

{

# Include "myheader. h"

... // Other C header files

}

This tells C ++ to compile the interpreter. The function foo is a C connection. You should find the name _ Foo in the library instead of _ foo_int_int. The C ++ compiler developer has processed the header files of the C standard library as extern "C", so we can use # include to directly reference these header files.

Note that not two functions with the same name can constitute an overload. The same name of a global function and a class member function is not considered a heavy load because the function has different scopes. For example:

Void print (...); // Global functions

Class

{...

Void print (...); // Member functions

}

Whether the parameters of the two print functions are different, if a member function of the class calls the global function print, the ':' flag should be added when the global function is called to distinguish it from the member function print. For example

: Print (...); // Indicates that print is a global function rather than a member function.

8.1.3 be careful that implicit type conversion will lead to ambiguity in heavy-duty functions

In Example 8-1-3, the first output function parameter is of the int type, and the second output function parameter is of the float type. Because the number itself does not have a type, it is automatically converted (called implicit type conversion) when it is treated as a parameter ). Statement output (0.5) will generate a compilation error because the compiler does not know whether to convert 0.5 to an int or float type parameter. Implicit type conversion can simplify program writing in many places, but it may leave hidden risks.

# Include <iostream. h>

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 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 ambiguity of the overloaded function

Member 8.2FunctionOverload, overwrite, and hide

The overload, override, and hiding of member functions are very confusing. C ++ programmers must understand the concept. Otherwise, errors will be hard to prevent.

8.2.1 heavy load and coverage

Features of member functions being overloaded:

(1) the same range (in the same class );

(2) The function name is the same;

(3) parameters are different;

(4) virtual keywords are optional.

Override refers to the function of a derived class that overwrites the base class function. The features are as follows:

(1) different scopes (located in the derived class and the base class respectively );

(2) The function name is the same;

(3) The parameters are the same;

(4) basic functions must have virtual keywords.

In Example 8-2-1, the Base: f (int) and Base: f (float) functions are overloaded with each other, while the Base: g (void) is Derived: g (void) overwrite.

# Include <iostream. h>

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 member function overload and overwrite

8.2.2 confusing Hidden Rules

It was not difficult to distinguish between overload and coverage, but the Hidden Rules of C ++ suddenly increased the complexity of the problem. Here, "hide" means that the function of the derived class shields the base class functions with the same name. The rules are as follows:

(1) If the function of the derived class has the same name as the function of the base class, but the parameter is different. In this case, functions of the base class will be hidden regardless of whether there is any virtual keyword (Be sure not to confuse them with overload ).

(2) If the function of the derived class has the same name and parameter as the function of the base class, but the base class function does not have the virtual keyword. In this case, the function of the base class is hidden (do not confuse with overwrite ).

(I think these two places should be paid attention to by everyone. At least I usually don't care much about these two places, and I often don't know .)

In Example Program 8-2-2 (:

(1) The Derived: f (float) function overwrites Base: f (float ).

(2) The Derived: g (int) function hides the Base: g (float) instead of the overload.

(3) The Derived: h (float) function hides Base: h (float) instead of overwrite.

# Include <iostream. h>

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, overwrite, and hide a member function

According to the author's investigation, many c ++ programmers do not realize that there is "hidden. Due to lack of deep understanding, the occurrence of "hiding" is a real failure and often produces confusing results.

In Example 8-2-2 (B), BP and DP point to the same address. The running result should be the same, but this 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) Comparison of overloading, overwriting, and hiding

8.2.3 get rid of hiding

Hiding rules causes a lot of trouble. In the example 8-2-3 program, the intention of the statement pd-> f (10) is to call the function Base: f (int), but the Base: f (int) is unfortunately Derived :: f (char *) is hidden. The number 10 cannot be implicitly converted into a string, so an error occurs during compilation.

Class Base

{

Public:

Void f (int x );

};

Class Derived: public Base

{

Public:

Void f (char * str );

};

Void Test (void)

{

Derived * pd = new Derived;

Pd-> f (10); // error

}

Example 8-2-3 error caused by hiding

From the Example 8-2-3, it seems silly to hide rules. However, there are at least two reasons for hiding rules:

The person who writes the statement pd-> f (10) may really want to call the Derived: f (char *) function, but he mistakenly writes the parameter wrong. With the hidden rules, the compiler can clearly point out errors. This is not necessarily a good thing. Otherwise, the compiler will quietly correct the error, and it will be difficult for programmers to find this error and cause a curse.

If the class Derived has multiple base classes (multiple inheritance), it is sometimes hard to figure out which base classes define function f. If no rule is hidden, pd-> f (10) may call an unexpected base class function f. Although hiding rules does not seem reasonable, they can indeed eliminate these accidents.

In Example 8-2-3, if the statement pd-> f (10) must call the function Base: f (int), 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

Some parameters have the same values in each function call. Writing such statements will get bored. C ++ uses the default parameter values to make writing concise (during compilation, the default value is automatically inserted by the compiler ).

Rules for using the default values of parameters:

L [Rule 8-3-1] The default value of the parameter can only appear in the declaration of the function, but not in the definition body.

For example:

Void Foo (int x = 0, int y = 0); // correct. The default value is displayed in the function declaration.

Void Foo (int x = 0, int y = 0) // error. The default value appears in the definition body of the function.

{

...

}

Why? I think there are two reasons: first, the implementation (Definition) of the function has nothing to do with whether the parameter has a default value, so it is not necessary to make the default value appear in the definition body of the function. Second, the default value of the parameter may be changed. Obviously, modifying the declaration of the function is easier than modifying the definition of the function.

L [Rule 8-3-2] If a function has multiple parameters, the parameter can only be left backward and backward by default. Otherwise, the function call statement is strange.

The correct example is as follows:

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

The error example is as follows:

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

Note that using the default values of parameters does not assign new functions to the function, but only simplifies the writing. It may improve the ease of use of functions, but may also reduce the comprehensibility of functions. Therefore, we can only use the default values of parameters as appropriate, So we should avoid negative effects caused by improper use. In Example 8-3-2, unreasonable use of the parameter's default value will lead to the ambiguity of the output function of the heavy load function.

# Include <iostream. h>

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

}

In the example, the default value of the 8-3-2 parameter causes the ambiguity of the overloaded function.

8.4 Operator Overloading

8.4.1 concepts

In C ++, operators can be added to the operator keyword to represent functions, which are called operator overloading. For example, two Aggregate functions:

Complex Add (const Complex & a, const Complex & B );

You can use the operator overload to represent:

Complex operator + (const Complex & a, const Complex & B );

The difference between an operator and a common function is that for a common function, the parameter appears in parentheses, and for an operator, the parameter appears on the left and right. For example

Complex a, B, c;

...

C = Add (a, B); // use a common function

C = a + B; // use the operator +

If the operator is overloaded as a global function, only one parameter operator is called a unary operator, and two parameter operators are called binary operators.

If the operator is overloaded as a member function of the class, the unary operator has no parameters. The binary operator has only one parameter on the right, because the object itself is a parameter on the left.

In terms of syntax, operators can be defined as both global functions and member functions. The article [Murray, p44-p47] has made a lot of elaboration on this problem, and summarized the rules of table 8-4-1.

Operator rules

We recommend that you reload all unary operators as member functions.

= () []-> Can only be reloaded as a member function

+ =-=/= * = & = | = ~ ==>>=<=We recommend that you reload the function as a member function.

We recommend that you reload all other operators as global functions.

Table 8-4-1 operator overload rules

Related Article

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.