High Quality C ++C Programming Guide

xiaoxiao2021-03-06  38

High Quality C / C Programming Guide

table of Contents

Preface

Chapter 1 File Structure *

1.1 Copyright and version of the declaration *

1.2 Structure of header file *

1.3 Defining the structure of the file *

1.4 Function of Head File *

1.5 directory structure *

Chapter 2 Program of the Program *

2.1 blank line *

2.2 code line *

2.3 Space in the code line *

2.4 Allowance *

2.5 Long Bank Split *

2.6 Position of the modifier *

2.7 Note *

2.8 class of layout *

Chapter 3 Name Rules *

3.1 Community Rules *

3.2 Simple Windows Application Names Rules *

3.3 Simple UNIX Application Name Rules *

Chapter 4 Expression and Basic Sentences *

4.1 Priority of the operator *

4.2 Composite Expression *

4.3 IF statement *

4.4 Efficiency of cyclic statements *

4.5 Loop Control Variables for For Statement *

4.6 Switch statement *

4.7 GOTO statement *

Chapter 5 constant *

5.1 Why do you need a constant *

5.2 Comparison of Const and #define *

5.3 Conveying Rules *

Constant in the 5.4 class *

Chapter 6 Function Design *

6.1 Rule of Parameter *

6.2 Return to the rule *

6.3 Rules within the function of functions *

6.4 Other Recommendations *

6.5 Using assertions *

6.6 Comparison of references and pointers *

Chapter 7 Memory Management *

7.1 Memory Allocation Way *

7.2 Common memory errors and their countermeasures *

7.3 Comparison of pointers and arrays *

7.4 How does the pointer parameter pass memory? *

7.5 How is Free and Delete how is the pointer? *

7.6 Does the dynamic memory are released automatically? *

7.7 Eliminate "wild pointer" *

7.8 Have a malloc / free still need new / delete? *

7.9 What should I do if the memory consumption? *

7.10 Malloc / Free Points *

7.11 New / Delete's Use Point *

7.12 Some experiences *

Chapter 8 Advanced Features of the C Function *

8.1 Function Overloaded Concept *

8.2 Overload, overlay and hidden in member functions *

8.3 Default value for parameters *

8.4 Operator Overload *

8.5 Function Inline *

8.6 Some experiences *

Chapter 9 Constructor, Destructor and Assignment Function *

9.1 Structure function and secting function *

9.2 Initialization Table of Constructor *

9.3 Senching of constructors and sectations *

9.4 Example: Constructor and destructor of class String *

9.5 Do not underestimate the copy constructor and assignment function *

9.6 Example: Copy Construction Function and Assignment Function of Class String *

9.7 Laying method to handle copy constructor and assignment function *

9.8 How to implement the basic function of the class in the derived class *

9.9 Some experiences *

Chapter 10 Inheritance and Combination of Class *

10.1 inheritance *

10.2 Combination *

Chapter 11 Other Programming Experience *

11.1 Using const to increase the robustness of the function *

11.2 Improve the efficiency of the program *

11.3 Some beneficial advice *

references *

Appendix A: C / C Code Review Form *

Appendix B: C / C Test *

Appendix C: Answer and Rating Standard for C / C Test *

1, this book is guided

First, please make the C / C test questions (do not look at the answer), how to examine your programming quality. Then refer to the answer strictly score. (1) If you have only dozens of points, please don't speak, don't be too sad. The difference between programming is often due to bad habits, with human intelligence, there is no major relationship, or has a medicine to save. The worse the results, the greater the space you can progress, is China not catching the developed capitalist country in the backward? As long as you can make a decision of bad program habits, the second exam will be able to comply. (2) If you have a test, it indicates that your technical foundation is good, I hope you can learn, constantly progress. If you haven't found a suitable work unit, you may wish to try it in Shanghai Bel. (3) If you have more than 85 points of good results, you have an obligation and qualification for your team as "C / C programming" training. I hope you can communicate with us and promote each other. I have found a good seedllar half a year ago, I dug him to our group. (4) If you have a full point in the case where there is no tip, I hope you can accept me to be your apprentice. After the programming exam is over, please read the text of this book. Chapter 1 to Chapter 6 of this book mainly discusses C / C programming style. The difficulty is not high, but the details are more. Don't underestimate, improve quality is to start from these little bit. There is no best programming style in the world, and everything is done due to demand. The team has developed the style. If you have developed your programming style, then all team members must follow. If the reader feels that the programming style of this book is more working, then use it, don't just don't do it. When people talk about it, they are not pronounced, and they will say, if they don't change, there is always regret. Programming is also the same. Chapters 7 to Chapter 11 is the topic discussion, technical difficulties are relatively high, and they should be actively thinking about reading. Especially Chapter VI, "Memory Management", reading does not understand, understanding, do not say it can be used correctly. One of her colleagues saw the seventh chapter, I felt that "wild pointer" was well written, and I took it with me. However, after two weeks, he told me that he was busy taking a bug for two days, and he wanted not to "wild pointers" out of the question, but he had to read the seventh chapter. Light is limited to the quality of programming, it is recommended that you read this book's reference, those are classic famous. Chapter 1 File Structure

Each C / C program is usually divided into two files. A file is used to save the program's declaration, called a header file. Another file is used to save the implementation of the program, called the definition file.

The header of the C / C program is ".h" as a suffix, the definition file of the C program is ".c" as a suffix, the definition file of the C program is usually ".cpp" as a suffix (there are also some systems ".cc" Or ".cxx" is a suffix).

1.1 Copyright and version declaration

Copyright and version of the statement is located at the beginning of the header file and the definition file (see Example 1-1), the main contents are:

(1) Copyright information.

(2) File name, identifier, summary.

(3) Current version number, author / modifier, complete date.

(4) Version history information.

/ * * Copyright (C) 2001, Shanghai ** Co., Ltd. Network Application Division * All Rights Reserved. * * File Name: FileName.h * File ID: See Configuration Management Program * Summary: Summary of this document * * current version: 1.1 * author: enter the author (or modified by) name * completion date: July 20, 2001 ** replace version: 1.0

* Original author: Enter the original author (or modified) Name * Completion date: May 10, 2001

* /

Example 1-1 Copyright and Version Declaration

1.2 structure of the header file

The header file consists of three parts:

(1) Copyright and version declaration at the beginning of the header (see Example 1-1).

(2) Preparation block.

(3) Functions and class structure declarations.

Assume that the header file name is graphics.h, the structure of the header file is shown in Example 1-2.

[Rule 1-2-1] In order to prevent the header files from being repeatedly referenced, the preparation block should be generated using the IFNDEF / DEFINE / ENDIF structure. [Rule 1-2-2] Use #include format to reference the standard library's header file (the compiler will start searching from the standard library directory). [Rule 1-2-3] Use a #include "filename.h" format to reference the unbalanced base file (the compiler will start searching from the user's working directory).

[Recommendation 1-2-1] Only "declaration" is stored in the header file without storing "definition"

In the C syntax, the members of the class can be defined while the declaration is defined and automatically becomes inline functions. Although this will bring the convenience of writing, but it has caused the style inconsistency, and the disadvantages are greater than the profit. It is recommended to separate the definitions and statements of the member function, regardless of how small the function is.

[Recommendation 1-2-2] Do not advocate global variables, try not to appear in the header file in the header files Extern int value.

// Copyright and version declaration See Example 1-1, omitted here.

#ifndef graphics_h / / Prevent Graphics.h from being repeatedly referenced

#define graphics_h

#include // Reference Standard Library's header file

...

#include "myHeader.h" // Reference the header file of the non-standard library

... void function1 (...); // global function declaration ... Class Box // class structure declaration {...};

#ENDIF

Example 1-2 Structure of C / C header file

1.3 Defining the structure of the file

The definition file has three parts:

Define the copyright and version declaration at the beginning of the file (see Example 1-1). Quote for some headers. The implementation of the program (including data and code).

Assuming that the name of the definition file is graphics.cpp, the structure of the definition file is defined in Example 1-3.

// Copyright and version declaration See Example 1-1, omitted here.

#include "graphics.h" // Reference header file

... // Global Function Implement Void Function1 (...) {...} // class member function's implementation VOID BOX :: Draw (...) {...

}

Example 1-3 C / C definition file structure

1.4 head file role

Early programming language such as Basic, Fortran does not have a header of the header, although the initiator of the C / C language will use the header file, but often unclear. The role of the header file here is slightly explained:

(1) Call the library function through the header file. In many cases, the source code is inconvenient (or not allowed) to publish to the user, as long as the header file and the binary library are available to the user. Users only need to call the library function according to the interface declaration in the header file without having to care about how the interface is implemented. The compiler will extract the corresponding code from the library. (2) Header file enhances type safety inspection. If an interface is implemented or used, its way is inconsistent with the statement in the header file, the compiler will point out errors, this simple rule can greatly reduce programmers debugging, and the burden of changing the wrong.

1.5 directory structure

If a software's header file is more (such as more than ten), the header file and definition file should usually be saved in different directories for maintenance.

For example, the header file can be saved in the include directory, and the definition file is saved in the Source directory (which can be a multi-level directory).

If some headers are private, it is not directly referenced by the user, and it is not necessary to disclose its "declaration". In order to enhance information hidden, these private headers can be stored in the same directory and definition files.

Chapter 2 Program

Although the layout does not affect the functionality of the program, it will affect readability. The layout of the program is clear, beautiful, and is an important constitutional factor in the style. You can compare the layout of the program as "calligraphy". A good "calligraphy" can make people look at the procedure, and it is very exciting. Poor program "calligraphy" such as crabs crawling, let people look like a taste, make more maintenance troubles. Please ask the programmer's "calligraphy" to make up for the loopholes of college computer education, it is necessary.

2.1 blank line

The air line plays a function of separating the program. Dedicated (more but less) will make the procedure layout clearer. The blank line does not waste memory, although printing the programs containing a blank line will consume more paper, but it is worth it. So don't bear to use blank lines.

[Rule 2-1-1] After each class declaration, the rows must be added after each function definition. See Example 2-1 (a) [Rule 2-1-2] In a function body, it is not vacuum line between the lack of relevant statements, and the line should be separated elsewhere. See example 2-1 (b)

// Dark Void Function1 (...) {...} // 空行 void function2 (...) {...} // 空 行 Void function3 (...) {...}

//

While (condition) {statement1;

//

IF (condition)

{Statement2;} else {statement3;}

//

Statement4;

Example 2-1 (a) Dark Sample 2-1 (b) Function between functions

2.2 code line

[Rule 2-2-1] One-line code only does one thing, if only one variable is defined, or write only a statement. This code is easy to read and is convenient to write a comment. [Rule 2-2-2] If, for, while, do, other words, the execution statement is not followed. No matter how much the execution statement is, {}. This prevents writing errors.

Example 2-2 (a) is a good style, examples 2-2 (b) are pool of poor style.

Int width; // Width int Height; // High INT DEPTH; // Depth Int Width, Height, Depth; // Width Height Depth X = A B; Y = C D; Z = E F; x = A B; Y = C D; Z = E F; if (width

[Recommendation 2-2-1] initialize the variable while defining variables (in principle)

If the reference to the variable is far apart from its definition, the initialization of the variable is easily forgotten. If the variable that is not initialized is referenced, it may cause a program error. This recommendation can reduce hidden dangers. E.g

INT width = 10; // Define and initiates Width

INT height = 10; // Definition and primary 绐 h

INT depth = 10; // Define and initiate DEPTH

2.3 Space in the code line

[Rule 2-3-1] leave spaces after keywords. At least one space is left after const, virtual, inline, case, etc., otherwise the keyword cannot be analyzed. Like if, for, while, I should leave a space and the left bracket '(' to highlight keywords. [Rule 2-3-2] Do not leave the space after the function name, keep track of the left bracket '(', In line with keywords. [Rule 2-3-3] '(' Tie, ')', ',', ';'; ',', close, close to space. [Rule 2-3 -4] ',' After you have space, such as Function (x, y, z). If ';' is not the end symbol, then leave space, such as for (Initialization; Condition; Update). [Rule 2 -3-5】 Assign value, compare operator, arithmetic operator, logical operator, bit domain operator, such as "=", " ="> = "," <= "," "," * ","% "," && "," | "," << "," ^ "should be added before and after the binary operator. [Rule 2-3-6] One yuan operator,"! " , "~", " ", "-", "&" will not add space before and after. [Rule 2-3-7] "[]", ".", "-> "These operators do not add spaces before and after.

[Recommendation 2-3-1] For a longer FOR statement and if statement, for the compact suction, some spaces can be removed, such as for (i = 0; i <10; i ) and IF (((a <= b) && (c <= d))

Void Func1 (int X, int y, int z); // Good style Void Func1 (int X, int y, int z); // bad style if (Year> = 2000) // Good style IF ( Year> = 2000) // Poor style IF ((a> = b) && (c <= d)) // Good style if (a> = b && c <= d) // bad style for (i = 0; I <10; i ) // Good style for (i = 0; i <10; i ) / defved style for (i = 0; i <10; i ) // too many spaces X = a function (); // Don't write B -> Function (); Example 2-3 Space in the code line

2.4 alignment

[Rule 2-4-1] The delimiter '{' and '}' of the program should be exclusively and in the same column, and the statement is left aligned with the statement that references them.

The code blocks within the rules 2-4-2] {} are aligned at the right side of '{' right.

Examples 2-4 (a) are aligned with good styles, examples 2-4 (b) are poor alignment.

Void function (int x) {... // program code} void function (int x) {... // program code} f (condition) {... // program code} else {... // program code} if (condition) { ... // Program Code} else {... // program code} for (Initialization; Condition; Update) {... // program code} for (Initialization; Condition; Update) {... //program code} while (condition) {... // Program Code} while (condition) {... // program code} If nest {}, indent alignment, such as: {... {...} ...}

Example 2-4 (a) Good style alignment example 2-4 (b) a bad alignment

2.5 Long Bank Split

[Rule 2-5-1] The maximum length of the code line should be controlled within 70 to 80 characters. Don't be too long, otherwise you can't see it, it is not convenient for printing. [Rule 2-5-2] Long expressions To split into new rows at low priority operators, operators are placed in the first of the new row (in order to highlight operators). The new line of split out should be properly indentation, so that the typography is neat, the statement is readable.

if ((very_longer_variable1> = very_longer_variable12) && (very_longer_variable3 <= very_longer_variable14) && (very_longer_variable5 <= very_longer_variable16)) {dosomething ();} virtual CMatrix CMultiplyMatrix (CMatrix leftMatrix, CMatrix rightMatrix); for (very_longer_initialization; very_longer_condition; very_longer_update) {dosomething ();} Example 2-5 Split of Long Row

2.6 Position of the modifier

The modifier * and & should be close to the data type or the near variable name is a controversial topic.

If the modifier * is close to the data type, for example: int * x; from semantics, it is more intuitive, ie X is the INT type pointer. The drawbacks of the above-mentioned ways are easy to cause misunderstandings, such as int * x, y; here Y is easy to be misunderstood as a pointer variable. Although the definition of X and Y branch can avoid misunderstanding, it is not to do so.

[Rule 2-6-1] should put the modifier * and & to close the variable name

E.g:

Char * name;

INT * X, Y; // This is not misunderstood

2.7 Notes

The annotation of the C language is "/ * ... * /". In the C language, the annotation of the block is often used in "/ * ... * /", and the line note is generally "// ...". Note is usually used in:

(1) Version, copyright statement;

(2) Function interface description;

(3) Important code line or paragraph tips.

Although the comment helps understand the code, be careful not to use a comment too much. See Example 2-6.

[Rule 2-7-1] Note is the "prompt" for the code, not a document. The comments in the program are unable to win, and there are too many comments that will be dazzled. There are fewer spelements. [Rule 2-7-2] If the code is originally clear, it is not necessary to add any comments. Otherwise, you will be bored. E.g

i ; // i plus 1, extra comment

[Rule 2-7-3] Write the code edge annotation, modify the code simultaneously modify the corresponding annotation to ensure the consistency of the annotation and code. Annotation that is no longer deleted. [Rule 2-7-4] The annotation should be accurate, easy to understand, and prevent the annuality of notes. The wrong note is not only harmful. [Rules 2-7-5] Try to avoid using abbreviations in the comments, especially uncommon-use. [Rule 2-7-6] The position of the comment should be adjacent to the code described, and can be placed above or the right side, and cannot be placed below. [Rule 2-7-8] When the code is relatively long, when there is multiple nested, it should be added at the end of some paragraphs, which is easy to read.

/ * * Function introduction: * Enter parameters: * Output parameters: * Return Value: * / void function (float x, float y, float z) {...} if (...) {... while (...) {...} // End of while ...} // end of ife

Example 2-6 Note

2.8 class layout

Categories can package data and functions together, where functions represent class behavior (or service). The class provides keyword public, protected, and private, which are used to declare which data and functions are public, protected or private. This can achieve the purpose of information hidden, that is, let the class only publicly have to let the outside world know, and hide all other content. We can't abuse the classes of the package, don't treat it as a hot pot, what is thrown in. There are two main ways of layouts:

(1) Write the data of the private type in front, and write the function of the public type in the back, as examples 8-3 (a). The programmer of this typography "is" centered ", focusing on the internal structure of the class.

(2) Write the function of the public type in front, and write the data of the private type in the back, as examples 8.3 (b) use this layout of programmers, the design "Behavior", focusing on What kind of interface (or service) should be provided.

Many C teaching books were influenced by Biarne Stroustrup's first book. Unconsciously adopted "data-centric" writing methods, and did not see how much.

I suggest that readers use the "Behaviors-centered" writing method, first considering what kind of function should be provided. This is the experience of many people & # 0; "Don't just let yourself think clearly when designing classes, but also convenient for others to read. Because users are most concerned about the interface, who is willing to see a pile of private data ! "

Class A {Private: INT I, J; FLOAT X, Y; ... PUBLIC: VOID FUNC1 (Void); Void Func2 (Void); ...} Class A {public: void func1 (void); void func2 (void); ... Private: INT I, J; Float X, Y; ...}

Example 8.3 (a) Taking Data-centric layout Example 8.3 (b) with behavior-centric layout

Chapter 3 Naming Rules

More famous naming rules When pushing Microsoft's "Hungary" method, the main idea of ​​this naming rule is "to add prefix in variables and function names to enhance people's understanding". For example, all character variables are prefixed in CH, and if the pointer variable is added, the prefix P is added. If a variable starts by the PPCH, it indicates a pointer to the character pointer.

The biggest disadvantage of "Hungary" method is cumbersome, such as I, J, K; Float X, Y, Z; if you use the Hungarian naming rule, you should write into int II, IJ, IK; // Prefix I represent int type Float FX, FY, FZ; // Prefix F indicates that the float type so cumbersome will make Most programmers can't stand. According to investigation, there is no name rule that allows all programmers to agree, and the programming textbooks generally do not specify naming rules. Naming rules are not "success or failure" for software products. We don't have much effort to present the best naming rules in the world, but should develop a naming rule that makes most project members, and Implementation in the project.

3.1 Community Rules

The commonal rules discussed in this section are adopted by most programmers, and we should expand specific rules, such as 3.2, under the premise of following these common rules.

[Rule 3-1-1] The identifier should be intuitive and can be spent, and it is expected that literacy is intended to "decode".

The identifier is preferably used in English words or its combination, which is easy to remember and read. Don't use Chinese Pinyin to name. The English words in the program generally don't be too complicated, and the words should be accurate. For example, don't write CurrentValue into NOWVALUE.

[Rule 3-1-2] The length of the identifier should comply with the principle of "min-length &&max-information". A few decades ago, the old ANSI C specified that the name was not allowed to more than 6 characters. Today's C / C no longer restrictions. In general, long names can better express me, so the function name, variable name, and class names are not blame. So is the longer the name? Do not see! For example, the variable name MaxVal is easy to use than MaxValueuntiloverflow. The name of a single character is also useful, common as i, j, k, m, n, x, y, z, etc., which are usually used as partial variables within the function.

[Rules 3-1-3] Naming rules should be consistent with the style of the operating system or development tool used.

For example, the identifier of the Windows application typically uses "case" mixing, such as addChild. The identifier of UNIX applications typically uses the "lowercase loopline" approach, such as add_child. Don't mix these two types of styles together.

[Rules 3-1-4] Do not appear similar identifiers that only rely on case in casement.

E.g:

INT X, X; // Variable X and X easy confusion

Void foo (int x); // Function foo is easy to confuse with foo

Void foo (float x);

【Rules 3-1-5】 Do not appear identifier exactly the same local variables and global variables, although both the scope of the two do not have a grammatical error, it will misunderstand.

[Rule 3-1-6] The name of the variable should use "noun" or "adjective noun".

E.g:

Float Value; float newvalue;

[Rules 3-1-7] The name of the global function should use "verb" or "verb noun". The member function of the class should only use "verbs", and the noun omitted is the object itself.

E.g:

Drawbox (); // global function

Box-> DRAW (); // member function

[Rules 3-1-8] Named the correct antisense word group named mutually exclusive variables or function of opposite action, and the like.

E.g:

Int minValue; int maxvalue;

Int setValue (...); int getValue (...);

[Recommendation 3-1-1] Try to avoid digital numbers in the name, such as Value1, Value2, etc. unless the number is logically required. This is to prevent programmers from being lazy, refuse to name the brains, resulting in the meaningless name (because of the most expensive thing).

3.2 Simple Windows Application Naming Rules

The author has reasonably simplified the "Hungary" naming rules, and the following naming rules are simple and easy to use, compare the development of Windows applications.

[Rule 3-2-1] Class name and function names are combined with a word starting with uppercase letters.

E.g:

Class node; // class name

Class LeafNode; // Classification

Void Draw (void); // Function Name

Void setValue (int value); // function name

[Rules 3-2-2] variables and parameters are combined with a case where lowercase letters begin.

E.g:

Bool flag;

Int drawMode;

[Rules 3-2-3] Constants use uppercase letters and segment words with underscore.

E.g:

Const int max = 100;

Const int max_length = 100;

[Rule 3-2-4] Static variable add prefix S_ (represents static).

E.g:

Void init (...) {

Static int s_initvalue; // static variable

...

}

[Rule 3-2-5] If the global variable is not required, the global variable is prefixed G_ (represents global).

E.g:

INT g_howmanypeople; // global variable

INT g_howmuchmoney; // global variable

[Rule 3-2-6] The data member of the class adds M_ (represents member), which avoids the same name of the data member and the member function.

E.g:

Void Object :: setValue (int width, int hotht)

{

m_width = width;

m_height = height;

}

[Rule 3-2-7] In order to prevent some identifiers in a single software library and conflicts in other software libraries, the prefix that can reflect software properties can be reflected in various identifiers. For example, all library functions of the 3D graphic standard OpenGL are starting with GL, all constants (or macro definitions) start with GL.

3.3 Simple UNIX Application Nameout

Chapter 4 Expression and Basic Sentences

Readers may doubt: even if IF, for, while, goto, Switch is simple things to explore programming style, is it a big question? I really think that many programmers write expressions and basic statements with implied errors, I have also made a similar error. Expressions and statements belong to the phrase structure syntax of C / C. They seem simple, but there are more hidden dangers when used. This chapter summarizes some rules and suggestions for correct use expressions and statements.

4.1 Priority of the operator

There are dozens of operators in C / C language, and the priority of the operator is shown in Table 4-1. Note that the priority of one yuan operator - * is higher than the corresponding binary operator.

Priority operator combined with high to low alignment () [] ->. From left to right! ~ - (type) SIZEOF - * & from right to left * /% from left to right - from Left to right << >> From left to right <<=>> = from left to right & from left to right ^ from left to right | from left to right && from left to right | From right to left?: From right to left = = - = * = / =% = & = ^ = | = << = >> = from left to right

Table 4-1 Priority and Combination Law of Operators

[Rules 4-1-1] If the operator in the code line is more, use parentheses to determine the order of operation of the expression, avoid using the default priority.

Because the Table 4-1 is more difficult, in order to prevent ambiguity and improve readability, the order of operation of the expression should be determined using parentheses. E.g:

Word = (High << 8) | Low IF ((A | B) && (A & C))

4.2 composite expression

As a expression of A = B = C = 0 is called a composite expression. The reason for allowing composite expressions is: (1) Clearing is simple; (2) can improve compilation efficiency. But to prevent abuse of composite expressions.

[Rule 4-2-1] Do not write too complex composite expressions.

E.g:

i = a> = B && c

[Rule 4-2-2] Do not have multi-purpose composite expressions.

E.g:

D = (a = b c) R;

This expression is given to a value of a value. Should be split into two independent statements:

A = B C;

D = a r; [Rule 4-2-3] Do not confuse composite expressions in the program with "real mathematical expressions".

E.g:

IF (a

Not expressed

IF ((a

Instead, it has become a puzzling

IF ((a

4.3 IF statement

The IF statement is the simplest, most common statement in the C / C language, but many programmers write IF statements in a hidden error. This section takes discussion to discuss this section with "comparison with zero value". 4.3.1 Boolean variables and zero value comparison

[Rule 4-3-1] The Boolean variable is not compared to TRUE, FALSE or 1, 0.

According to the semantics of the Boolean, the zero value is "false" (written as false), any non-zero value is "true" (recorded by true). What is the value of TRUE does not have a unified standard. For example, Visual C defines TRUE to 1, and Visual Basic defines TRUE as -1.

Suppose the Boolean variable name is FLAG, it is compared with the zero value, the standard IF statement is as follows:

IF (flag) // indicates that Flag is true

If (! flag) // means flag is false

Other usage belong to a bad style, for example:

IF (Flag == True)

IF (Flag == 1)

IF (Flag == False)

IF (Flag == 0)

4.3.2 Integer variables and zero value

[Rule 4-3-2] The integer variable should be compared to 0 with "==" or "! =".

Assuming the name of the integer variable is Value, it is the standard IF statement compared to zero value as follows:

IF (value == 0)

IF (value! = 0)

Written in the style of imitation of the Boolean variable

If (value) // will misunderstand value is a Boolean variable

IF (! value)

4.3.3 Comparison of floating point variables and zero values

[Rule 4-3-3] Do not compare floating point variables with "==" or "! =" With any number.

Need to pay attention, whether Float is also a Double type variable, it has precision restrictions. Therefore, be sure to avoid "==" or "! =" And digital comparisons, should be triggered into "> =" or "<=" form. Assume that the name of the floating point variable is x, it should

IF (x == 0.0) // Comparison of incorrect errors

change into

IF ((x> = - EPSINON) && (x <= epinon))

Where EPSINON is allowed (ie, accuracy). 4.3.4 Pointer variables and zero value comparison

[Rule 4-3-4] The pointer variable should be compared with NULL with "==" or "! =".

The zero value of the pointer variable is "empty" (recorded as null). Although the value of NULL is the same as 0, the meaning is different. Suppose the name of the pointer variable is P, which is the standard IF statement compared to zero value as follows:

IF (p == null) // p and NULL explicitly compared, emphasizing P is a pointer variable if (p! = null) Do not write into IF (p == 0) // Easy to misunderstand P is integer variable IF ( P! = 0) or

IF (p) // Easy to misunderstand P is Boolean variable

IF (! p) 4.3.5 Supplementary to the IF statement This time we may see if (null == p) so weird format. It is not the wrong program, which is the programmer to prevent P and NULL to reverse P and NULL in order to prevent IF (p == null) from being written into IF (p = null). The compiler believes that if (p = null) is legal, it will indicate that if (null = p) is wrong because NULL cannot be assigned. The combination of IF / ELSE / RETURN is sometimes encountered in the program, and the procedure IF (condition) should be made

Return X;

Return Y;

Rewrite

IF (condition)

{

Return X;

}

Else

{

Return Y;

}

Or rewritten into more concise

RETURN (CONDition? x: y);

4.4 Efficiency of cyclic statements

In the C / C cycle statement, the FOR statement uses the highest frequency, and the While statement is second, the DO statement is rarely used. This section focuses on the efficiency of the cyclic body. The basic approach to improving the efficiency of cyclic body is to reduce the complexity of the circulation.

[Recommendation 4-4-1] In multiple cycles, if possible, the longest cycle should be placed in the innermost layer, and the shortest cycle is placed in the outermost layer to reduce the number of CPU cross-cutting circulation layers. For example, the efficiency ratio of Example 4-4 (b) is higher than the example 4-4 (a).

For (row = 0; ROW <100; ROW ) {

For (COL = 0; Col <5; Col ) {Sum = Sum a [ROW] [col]

}

} for (COL = 0; col <5; col ) {

For (ROW = 0; ROW <100; Row )

{

SUM = SUM A [ROW] [col];

}

}

Example 4-4 (a) Low Efficiency: Long cycle in the outermost example 4-4 (b) High efficiency: long cycle in the innermost layer

[Recommendation 4-4-2] If there is a logic judgment in the circulation, the number of cycles is large, and the logic is appropriately moved to the outside of the cyclic body. Example 4-4 (c) of the program is multi-1 logic judge more than the example 4-4 (d). And because the former is logical judgment, the loop "pipeline" operation is interrupted, so that the compiler cannot optimize the loop, reducing efficiency. If N is very large, it is best to use the writing of Example 4-4 (d) to improve efficiency. If N is very small, the difference between the two is not obvious, and the writing method of Example 4-4 (c) is better because the program is more concise.

For (i = 0; i

Table 4-4 (c) Low efficiency but program simplicity table 4-4 (d) is high, but the program is not simple

4.5 Loop control variable for for statement

[Rule 4-5-1] The loop variable cannot be modified within the FOR cycle, preventing the FOR cycle from being loses control.

[Recommendation 4-5-1] Recommended the value of the loop control variable of the FOR statement using the "semi-open semi-closed interval" Writing.

The X value in Example 4-5 (a) belongs to the semi-open semi-closed interval "0 =

Example 4-5 (a) The cyclic variable belongs to the semi-open semi-closed interval example 4-5 (b) cyclic variable belongs to the closed section

4.6 Switch statement

Is there a Switch statement in the IF statement? Switch is a multi-branch selection statement, while if the IF statement is available for selection. Although multi-branch selection can be implemented with nested IF statements, the programs are long-term difficulty. This is the reason for the Switch statement. The basic format of the Switch statement is:

Switch (variable)

{

Case Value1: ...

Break;

Case Value2: ...

Break;

...

DEFAULT: ...

Break;

}

[Rule 4-6-1] Do not forget to add BREAK at the end of each case statement, otherwise multiple branch overlaps (unless you intend to overlap multiple branches). [Rule 4-6-2] Don't forget the last Default branch. Even if the program really does not need Default processing, the statement Default: Break should also be reserved. Do not do more, but to prevent others from mistaken to think that you forgot DEFAULT.

4.7 GOTO statement

Since advocating structured design, Goto has become a controversial statement. First, because the goto statement can be flexibly jump, if it is not restricted, it does destroy the structured design style. Second, the GOTO statement often brings errors or hidden dangers. It may skip some of some objects, initialization of variables, important calculations, etc., for example:

Goto State;

String S1, S2; // Skip by GOTO

Int sum = 0; // Skip by GOTO

...

State:

...

If the compiler can't find such errors, every GOTO statement may leave a hidden danger. Many people recommend abolishing C / C's goto statement, in order to suffer. But seeking truth from facts, error is caused by programmers, not goto's fault. At least one of the GOTO statements, can be jumped from the middle of the multi-cycle body, and don't write a lot of BREAK statements; for example

{...

{...

{...

Goto error;

}

}

}

Error:

...

Just like the fire is in fire, I will not go down from the first level of the stairs to jump out of the fire pit from the window. So we advocate less, cautious with GOTO statements, not disabled.

Chapter 5 constant

Constants is an identifier that is constant during operation. C language uses #define to define constants (called a macro regular). C language can also be defined with constance (called Const) in addition to #define.

5.1 Why do I need a constant 5.1 Why do you need a constant 5.1 Why do you need constants?

If you do not use constants, fill in the numbers or strings directly in the program, what will happen?

The readability of the program is deteriorated. The programmer will forget what the figures or strings mean, and the user doesn't know where they come, what is it. In many places of the program, enter the same number or string, and it is difficult to keep writing errors. If you want to modify the number or string, it will be changed in many places, which is both trouble and easy. [Rules 5-1-1] Try to use meanly intuitive constants to indicate those numbers or strings that will appear multiple times in the program.

For example: #define max 100 / * C language Macroembox * /

Const int max = 100; // c language constant constant

Const float pi = 3.14159; // C language constant constant

5.2 Comparison of Const and #define

The C language can define constants with const, or use #define to define constants. But the former has more advantages over the latter:

The constant constant has a data type, while the macro constant has no data type. The compiler can perform type security checking for the former. For the latter only characters, there is no type of security check, and it may generate unexpected errors (marginal effects) in characters. Some integrated debugging tools can debug a Const constant, but cannot debug a macro.

[Rules 5-2-1] Only C programs use only constants without using a macro pattern, that is, the constant constant is completely replaced.

5.3 Constant definition rules

[Rule 5-3-1] Requires the constant of the foreign publicly placed in the header file, and does not require the heads of the definition file to the definition file. To facilitate management, you can store the constant concentration of different modules in a common header file. [Rule 5-3-2] If a constant is closely related to other constants, this relationship should be included in the definition, and some isolated values ​​should be given.

E.g:

Const float radius = 100;

Const float diameter = radius * 2;

Constant in the 5.4 class

Sometimes we hope that some constants are only valid in the class. Because the macroemulus defined by #define is global, it cannot achieve the goal, so it is to certainly feel that it should be implemented with a const modified data member. The Const data member does exist, but its meaning is not what we expect. Const data members are constants only within an object survival, but for the entire class, it is variable because the class can create multiple objects, and different objects can be different. CONST data members cannot be initialized in class declarations. The following usage is wrong, because the compiler does not know what the value of size is not created. Class a {... const Int size = 100; // error, attempt to initialize const data member int array [size]; // error, unknown size}; Const data member can only be in class constructor In the initialization table, such as Class A {... a (int size); // constructor const INT size;}; a :: a (int size): initial table {...} a A (100); // The size value of the object A is 100 A b (200); // // The Size value of object B is 200 how can it be established in the entire class constant constant? Don't expect const data members, you should implement the enumeration constants in the class. For example, Class A {... enum {size1 = 100, size2 = 200}; // enumeration constant int Array1 [size1]; int Array2 [size2];}; enumeration often does not take up the object's storage, they are compiled It is all given up. The disadvantage of the enumeration is that its implied data type is an integer, its maximum value is limited, and cannot represent floating point numbers (such as PI = 3.14159). Chapter 6 Function Design

The function is the basic functional unit of the C / C program, and its importance is self-evident. Function design is easy to cause this function to be misuse, so the function of the function is not enough. This chapter focuses on the interface design and internal implementation of the function.

Two elements of the function interface are parameters and return values. In the C language, there are two ways to pass the parameters and return values: Value Pass By Value and Packing By Pointer. The C language has more reference delivery (pass by reference). Since the nature of the reference passed, the use of the way is transmitted, and the initiator is often confused, it is easy to cause confusion, please read the 6.6 "reference and pointer comparison".

6.1 rules of parameters

[Rule 6-1-1] The writing of parameters should be complete, do not save the type of parameters and omit the parameter name. If the function does not have a parameter, fill it with VoID.

E.g:

Void setValue (int width, int hotht); // Good style

Void setValue (int, int); // bad style

Float getValue (void); // Good style

Float getValue (); // bad style

[Rule 6-1-2] The parameter is naming is appropriate, and the order is reasonable.

For example, writing a string copy function StringCopy, it has two parameters. If the parameter name is started with STR1 and STR2, for example

Void stringcopy (char * str1, char * STR2);

So, it is difficult to figure out to copy Str1 to STR2, or just poured.

You can make the parameter name more meaningful, such as strsource and strDestination. This can be seen from the name, you should copy the strsource to strDestination. There is also a problem, the two parameters should this be the one before? The order of the parameters should follow programmers' habits. In general, the destination parameters should be placed in front, and the source parameters should be placed behind. If the function is declared as:

Void stringcopy; char * strdestination;

Others may not think about the following form when using others:

Char Str [20];

Stringcopy (STR, "Hello World"); // Parameter sequence reverse

[Rule 6-1-3] If the parameter is a pointer, it is only used for input, it should be plus const in the type, to prevent the pointer from being accidentally modified in the function.

E.g:

Void stringcopy (char * strdestination, const char * strsource);

[Rule 6-1-4] If the input parameter passes an object in a value transfer, it should be transferred to the "const" mode to save the construction and secting process of the temporary object, thereby increasing efficiency.

[Recommendation 6-1-1] Avoiding the function of too much parameters, the number of parameters is controlled within 5. If the parameters are too much, it is easy to make the parameter type or order when used.

[Recommendation 6-1-2] Try not to use the type and number of uncertain parameters.

C Standard Library Function Printf is a typical representative using uncertain parameters, its prototype:

INT Printf (const chat * format [, argument] ...);

This style of function has lost strict type safety inspections at compile.

6.2 Return value rules

[Rule 6-2-1] Do not omit the type of return value.

C language, where you do not add the type of function, you will automatically press the integer. Don't do this, it is easy to be misunderstood as a Void type.

The C language has a strict type security check that does not allow the above situation. Since the C program can call the C function, in order to avoid confusion, it is necessary to have a type of C / C function. If the function does not return value, it should be declared as a Void type.

[Rule 6-2-2] Function name and return value type are unable to conflict in semantics.

A typical representative of this rule is a C standard library function getchar.

E.g:

Char C;

C = getchar ();

IF (c == EOF)

...

According to the GetChar name, the variable C is declared as a CHAR type is a natural thing. But unfortunately, getChar is indeed not a char type, but the int type, its prototype is as follows:

INT getchar (void);

Since C is a char type, the value range is [-128, 127], if the value of the macro EOF is outside the value of char, then the IF statement will always fail, this "danger" people generally get it! The responsibility of this error is not in the user, is the function getchar misleading the user.

[Rule 6-2-3] Do not return normal values ​​and error flags together. Normal values ​​are obtained with output parameters, and the error flag returns with the return statement.

Review the above example, why do the designers of the C standard library function be declared as a confused int type? Will he be so stupid?

Under normal circumstances, getChar does return to a single character. But if getchar hits the file end flag or a read error, it must return a flag EOF. To distinguish between normal characters, you have to define EOF as a negative number (usually negative 1). Therefore, the function getchar is an int type. In actual work, we often encounter the above problems. In order to avoid misunderstandings, we should separate the normal values ​​and error signs. That is, the normal value is obtained with the output parameters, and the error flag returns with the return statement.

The function getchar can be rewritten into BOOL getchar (char * c);

Although gechar is flexible than getchar, such as Putchar (GetChar ()); however, if getchar is wrong, what is its flexibility?

[Recommendation 6-2-1] Sometimes the function does not need to return value, but in order to increase flexibility, such as support chain expression, you can add a return value.

For example, string copy functions STRCPY prototype:

Char * STRDEST, Const Char * strsrc);

The STRCPY function copies the strsrc to the output parameter strDest, and the return value of the function is STRDEST. Do not do more, you can get the following flexibility:

Char Str [20];

INT Length = Strlen (Str, "Hello World");

[Recommendation 6-2-2] If the return value of the function is an object, some occasions can be used to replace "value transfer" with "reference delivery" to improve efficiency. Some occasions can only be used to deliver "without" reference delivery ", otherwise it will be wrong.

E.g:

Class String

{...

// Assignment function

String & Operate = (const string&);

// Add functions, if there is no Friend modification, only one right parameter

Friend String Operate (Const String & S1, Const String & S2);

Private:

CHAR * M_DATA;

}

String's assignment function operate = The implementation 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; // Returns * this reference, no copy process

}

For assignment functions, the String object should be returned by "reference delivery". If the function is passed correctly, although the function is still correct, because the RETURN statement is copied to the external storage unit of the returned value, it adds unnecessary overhead to reduce the efficiency of the assignment function. E.g:

String A, B, C;

...

A = b; // If you use "value transfer", you will generate a * this copy

A = b = c; // If you use "value transfer", you will generate twice * this copy

The implementation of the String additive function Operate is as follows:

String Operate (Const String & S1, Const String & S2)

{

String Temp;

Delete temp.data; // Temp.data is only & # 0; / 0 & # 0; string temp.data = new char [strlen (s1.data) Strlen (S2.DATA) 1];

STRCPY (Temp.Data, S1.DATA);

STRCAT (TEMP.DATA, S2.DATA);

Return Temp;

}

For additive functions, the String object should be returned by "value delivery". If you switch to "reference delivery", the function return value is a "reference" pointing to the local object TEMP. Since Temp is automatically destroyed at the end of the function, the return "reference" will result in invalid. E.g:

C = a b; at this time, A B does not return to the expected value, and C is not there, and there is a hidden danger.

6.3 Rules for internal implementation

Different functions have different internally realization, it seems that it is not possible to agree on "internal implementation". But according to experience, we can strictly control the quality of the function in the "entrance" and "exit" of the function body.

[Rule 6-3-1] Check the validity of the parameters in the "entrance" of the function body.

Many program errors are caused by illegal parameters, we should fully understand and correctly use "assert" to prevent such errors. See "Using Assessment" in Section 6.5.

[Rule 6-3-2] In the "exit" of the function body, check the correctness and efficiency of the Return statement.

If the function has a return value, the "exit" of the function is a returnite statement. Don't underestimate the Return statement. If the return statement is not written, the function is either error, or it is low.

Note is as follows:

(1) RETURN statement cannot return "pointer" or "reference" to "stack memory", because the internal existence is automatically destroyed. E.g

Char * func (void) {char str [] = "Hello World"; // STR's memory is on the stack ... Return str; // will result in an error}

(2) To figure out the "value", "pointer" is still "reference".

(3) If the function return value is an object, consider the efficiency of the Return statement. E.g

Return String (S1 S2);

This is the syntax of the temporary object, representation "Create a temporary object and return it." Don't think that it is equivalent to "first creation of a local object Temp and returns it" is equivalent, such as

String Temp (S1 S2);

Return Temp;

Essentially, three things will occur. First, the TEMP object is created and is initialized; then the copy constructor copies the TEMP to the external storage unit that saves the return value; finally, TEMP is destroyed at the end of the function (call destructor). However, "Creating a temporary object and returning it" is different. The compiler creates the temporary object directly and initializes the cost of copying and sectulating in the external storage unit, and improving efficiency.

Similarly, we don't want

Return Int (x y); // Create a temporary variable and return it

Write

INT TEMP = X Y;

Return Temp;

Due to internal data types such as int, float, Double variables do not have constructor and destructive functions, although the "temporary variable syntax" does not increase how much efficiency, but the program is more simple and easy to read.

6.4 Other recommendations

[Recommendation 6-4-1] The function of the function is single, do not design a multi-purpose function. [Recommendation 6-4-2] The scale of the function body is small, and the minimum is controlled within 50 lines of code. [Recommendation 6-4-3] Try to avoid the function with the "memory" function. The same input should produce the same output. A function with the "memory" function, its behavior may be unpredictable because its behavior may depend on some "memory state". Such functions are neither easy to understand and are not conducive to testing and maintenance. In the C / C language, the STIC local variable of the function is the "memory" memory of the function. It is recommended to use the Static partial variable as possible unless it is necessary.

[Recommendation 6-4-4] Not only should you check the validity of the input parameters, but also check the effectiveness of variables in the function in the function of other routes, such as global variables, file handles, etc. [Recommendation 6-4-5] The return value for error handling must be clear, so that the user does not easily ignore or misunderstand the error.

6.5 use assertion

The program is generally divided into debug version and release version, and the Debug version is used for internal debugging, and the Release version is issued to the user.

Assert is a macro that works only on the Debug version, it is used to check the situation that "should not" happen. Example 6-5 is a memory replication function. During the run, if the parameter of Assert is a false, the program will abort (generally show the dialogue, which means where ASSERT is triggered).

Void * Memcpy (void * pvfrom, size_t size) {assert ((pvfrom! = null) && (pvfrom! = null)); // Using assertion BYTE * PBTO = (Byte *) PVTO; // Prevent the address of the PVTO byte * pbfrom = (byte *) PVFROM; / / Prevents the address of the PVFROM from the address while (size -> 0) * PBTO = * PBFROM ; Return PVTO;

Example 6-5 Copying the memory blocks that do not overlap

Assert is not a macro of a rush. In order not to cause a difference between the Debug version and the Release version, Assert should not generate any side effects. So Assert is not a function, but a macro. Programmers can see Assert as a honey-impaired test means that can be safely used in any system state. If the program terminates at Assert, it is not to say that the function containing the Assert has an error, but the caller has an error, and Assert can help us find the cause of the error.

I rarely compare the assertion of the procedure, but I don't know if the role of the assert is more depressed. You have been a lot of time, not to troubleshoot, but just to figure out what this error is. Sometimes, programmers can occasionally design errors. So if you can't figure out what is checked, it is difficult to judge that the error is in the program, or it appears in the assertion. Fortunately, this problem is very good, just add a clear annotation. This is an obvious thing, but there are very few programmers. This is better than a person in the forest, seeing a "dangerous" big brand on the tree. But what is dangerous? Do you want to fall? Is there a waste well? Is there a beast? This warning card is difficult to play an active effect unless it tells people "dangerous". It is difficult to understand that it is often overlooked by programmers or even deleted. [MAGUIRE, P8-P30]

[Rule 6-5-1] Use asserts to capture illegal conditions that should not occur. Do not confuse the difference between illegal conditions and error conditions, the latter inevitably exist and must be processed. [Rule 6-5-2] At the entrance to the function, the validity of the assertion check parameters (legitimacy) is used. [Recommendation 6-5-1] When writing a function, it is necessary to make a repeated exam, and ask yourself: "What assumes I plan?" Once the assumption is determined, it is necessary to check the assumption using the assertion. [Recommendation 6-5-2] General textbooks encourage programmers to perform anti-wrong design, but remember that this programming style may hide errors. When the error is performed, if the "impossible" thing does occur, you should use the assertion to conduct alarm. 6.6 Reference and pointer comparison

Quote is the concept in C , and beginners are easy to confuse the references and pointers. In the program, N is a reference to M, and m is a reference (REFERENT).

Int m;

INT & n = m;

n is equivalent to the alias (nickname), any operation to N is the operation of M. For example, some people named Wang Xiaoyao, his nickname is "three hair". Saying how "San Mao" is, in fact, it is three four four. So n is neither a copy of M, nor pointing to M's pointers, in fact, N is M itself. Some of the rules that are referenced are as follows: (1) Quote must be initialized while being created (pointer can be initialized at any time). (2) There is no NULL reference, the reference must be associated with the legal storage unit (the pointer can be null). (3) Once the reference is initialized, the reference is not changed (the pointer can change the object you refer to time). In the following example program, K is initialized to i's reference. Statement k = j does not modify K into J. The value of K is changed to 6. Since K is a reference to i, the value of i has also become 6.

INT i = 5;

INT j = 6;

INT & K = I;

K = j; // k and i have the value of 6;

The above program looks like playing text games, does not reflect the value of reference. The main function of the reference is to transfer the parameters and return values ​​of the function. In the C language, there are three ways to pass the parameters and return values: value transfer, pointer delivery, and reference delivery. The following is an example program for "value delivery". Since X in the FUNC1 function body is a copy of the external variable N, the value of the X does not affect N, so N's value is still 0.

Void func1 (int X)

{

X = x 10;

}

...

INT n = 0;

Func1 (N);

COUT << "n =" << n << endl; // n = 0

The following is an example program for "pointer". Since x in the FUNC2 function is pointed to the external variable N, the content that changes the pointer will cause the value of N to change, 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 an example program that "reference delivery". Since X in the FUNC3 function body is referenced by the external variable N, X and N are the same thing, and the change X is equal to changing N, so N is the value of 10.

Void Func3 (int & x) {

X = x 10;

}

...

INT n = 0;

FUNC3 (N);

Cout << "n =" << n << endl; // n = 10 contrast the above three sample programs, will find the nature of "reference delivery" like "pointer delivery", and writing the way "value transfer". In fact, anything "pointer" that can be done can also do, why should I "quote"? The answer is "Doing the right tool to work with appropriate tools." The pointer can operate how things in memory without restraint, although the pointer is powerful, it is very dangerous. Just like a knife, it can be used to cut trees, paper cuts, manicure, haircut, etc., who dares to use it? If you only need to borrow a "alias" of an object, then use "reference", not to use "pointer" to avoid accidents. For example, someone needs a certificate that I originally opened the official seal on the document. If you give him the key to him, then he gains the right.

Chapter 7 Memory Management

Welcome to the memory of the mine. Great Bill Gates once lost:

640k Ought to Be ENOUGH for Everybody

& # 0; BILL GATES 1981

Programmers often write memory management procedures, often hanging. If you don't want to touch it, the only solution is to find all lurking mines and exclude them, hiding is hiding. The content of this chapter is much more in-depth than the general textbook. The reader needs to read it carefully, and doing truly a lot of memory management.

7.1 Memory allocation method

There are three ways of memory allocation:

Assign from static storage area. There is already a hidden when there is program compilation, and there is existence of the entire running period of the program. For example, global variables, static variables. Create on the stack. When performing a function, the storage unit of local variables within the function can be created on the stack, and the memory cells are automatically released at the end of the function. The stack memory allocation operation is within the command set of the processor, the efficiency is high, but the assigned memory capacity is limited. Allocate from the pile, also known as dynamic memory allocation. The program applies for any number of memory with Malloc or New when running, and the programmer will release the memory with free or delete. Dynamic memory survival is determined by us, it is very flexible, but the problem is most.

7.2 Common memory errors and their countermeasures

It is very troublesome to have a memory error. The compiler cannot automatically discover these errors, usually captured when the program is running. Most of these errors do not have obvious symptoms, and sometimes the difficulty of inclination is increased. Sometimes the user is angry, and the program has not happened, you will walk, the mistake is also episodes. Common memory errors and their countermeasures are as follows:

Memory allocation is unsuccessful, but it uses it.

Programming newcomers often make this mistake because they don't realize that memory allocation will be unsuccessful. Commonly used solution is to check if the pointer is NULL before using the memory. If the pointer P is the parameter of the function, use Assert (P! = NULL) to check at the entry of the function. If you are using Malloc or New to apply for memory, you should use if (p = null) or if (p! = Null).

Memory allocation is successful, but it is not initialized to reference it. There are two main causes of this mistake: one is the concept of uniniterated; the second is that the default initial value of memory is all zero, causing the reference initial value error (such as arrays). The default initial value of memory does not have a unified standard, although some time is zero, we would rather believe that it is untrustworthy. So don't forget to assign initial values, even if you are ingredient, you should not be troublesome. Memory allocation is successful and has been initialized, but the operation crosses the boundary of the memory.

For example, when using an array, the subscript "more 1" or "less 1" is often generated. Especially in the For cycle statement, the number of cycles is easy to make mistakes, causing array to operate the offshore.

Forgot to release memory, causing memory leaks. A function containing such an error is lost once a time is called once. At the beginning, the system's memory is sufficient, you can't see the mistake. I have finally dead, and the system prompts: memory consumption.

Dynamic memory application and release must be paired, the number of malloc and free usage will be the same, otherwise there must be an error (New / Delete.).

Release memory but continues to use it. Three cases:

(1) Object call relationship in the program is too complicated, and it is difficult to figure out whether an object has released memory. At this time, the data structure should be redesigned, fundamentally solve the chaotic situation of the object management.

(2) The RETURN statement of the function is wrong. Be careful not to return "pointer" or "reference" pointing to "Stack Ins), because the internal existence is automatically destroyed.

(3) After using Free or Delete releases the memory, the pointer is not set to NULL. Resulting in a "wild pointer".

[Rule 7-2-1] After using Malloc or New to apply for memory, it should immediately check if the pointer value is NULL. Prevent memory using the pointer value of NULL. [Rule 7-2-2] Don't forget to be a first value for array and dynamic. Prevent memory that will not be initialized as the right value. [Rule 7-2-3] Avoid the subsidiaries of array or pointer, especially "more than 1" or "less 1" operation occurs. [Rule 7-2-4] The application and release of dynamic memory must be paired to prevent memory leakage. [Rule 7-2-5] After using Free or Delete released memory, set the pointer to NULL to prevent "wild pointer".

7.3 Comparison of pointers and arrays

In C / C procedures, pointers and arrays can replace each other in many places, making people an illusion that they are equivalent.

Arch is either created in a static memory area (such as global array) or created on the stack. The array name corresponds to (not pointing) a piece of memory, its address and capacity remain unchanged within the lifetime, only the content of the array can be changed. The pointer can always point to any type of memory block, and its feature is "variable", so we often use pointers to operate dynamic memory. The pointer is far more flexible than the array, but it is also more dangerous. The characteristics of the pointer and array are compared with a string as an example.

7.3.1 Modify Content

In Examples 7-3-1, the capacity of the character array A is 6 characters, and its content is Hello / 0. The content of A can be changed, such as A [0] = 'x'. The pointer P pointing to the constant string "World" (located in a static storage area, content / 0), and the content of the constant string cannot be modified. From the grammatical point of view, the compiler does not feel that the statement P [0] = 'x' is in any, but this statement is attempting to modify the content of the constant string and cause the run error.

Char a [] = "Hello";

a [0] = 'x';

COUT << a << endl; char * p = "world"; // Note P pointing to constant strings

P [0] = 'x'; // The compiler cannot find the error

Cout << p << endl;

Examples 7-3-1 Modify the contents of arrays and pointers

7.3.2 Content replication and comparison

You cannot directly copy and compare the array names. In Examples 7-3-2, if you want to copy the contents of the array A to an array B, you cannot use statement b = a, otherwise the compilation error will be generated. You should use the standard library function STRCPY to be copied. The same, the contents of comparison B and A are the same, and cannot be judged by IF (b == a), and should be compared with the standard library function STRCMP. The statement P = A does not copy the contents of the A, but assigns the address of A to P. To copy A content, you can first use the library function Malloc to apply a capacity of Strlen (a) 1 characters, and then use Strcpy to replicate. Similarly, statement if (p == a) is not content but addresses, should be compared with library functions strCMP.

// array ... CHAR A [] = "Hello"; char b [10]; strcpy (b, a); // cannot use B = a; if (strcmp (b, a) == 0) // cannot be used IF (b == a) ... // pointer ... int LEN = Strlen (a); char * p = (char *) malloc (sizeof (char) * (len 1)); STRCPY (P, A); / / Do not use p = a; if (strCMP (p, a) == 0) // Do not use IF (p == a) ...

Example 7-3-2 Content replication and comparison of arrays and pointers

7.3.3 Computation Memory Capacity

Use operator SIZEOF to calculate the capacity of the array (number of bytes). In Examples 7-3-3 (a), the value of SIZEOF (A) is 12 (notice that don't forget '/ 0'). Pointer P pointing A, but the value of SizeOf (p) is 4. This is because SIZEOF (P) is a number of bytes of a pointer variable, which is equivalent to sizeof (char *) instead of the memory capacity referred to. The C / C language has no way to know the memory capacity referred to by the pointer unless you remember it when you apply for memory.

Note that when the array is passed as a parameter of the function, the array automatically degenerates into the same type of pointer. In Examples 7-3-3 (b), SIZEOF (A) is always equal to SIZEOF (CHAR *) regardless of the capacity of array a.

Char a [] = "Hello World"; char * p = a; cout << sizeof (a) << Endl; // 12 byte cout << sizeof (p) << endl; // 4 byte

Examples 7-3-3 (a) Calculate the memory capacity of arrays and pointers

Void Func (Char A [100]) {cout << sizeof (a) << endl; // 4 byte rather than 100 bytes}

Examples 7-3-3 (b) Arrange Degradation is a pointer

7.4 How does the pointer parameter pass memory?

If the parameter of the function is a pointer, do not expect to use the pointer to apply for dynamic memory. Examples 7-4-1, the statement of the TEST function GetMemory (STR, 200) did not enable Str to get the desired memory, STR is still null, why? Void getMemory (char * p, int num) {p = (char *) malloc (sizeof (char) * NUM);

}

Void test (void) {char * str = null; getMemory (STR, 100); // Str Still null struct (STR, "Hello"); // run error}

Example 7-4-1 Attempt to apply for dynamic memory with pointer parameters

The problem is in the function GetMemory. The compiler always makes a temporary copy for each parameter of the function, the copy of the pointer parameter P is _P, the compiler makes _p = P. If the program in the function is modified, the contents of the parameter P are modified accordingly. This is why the pointer can be used as an output parameter. In this example, _P applies for new memory, just changing the memory address referred to in _P, but p is unchanged. So the function getMemory does not output anything. In fact, every time GetMemory will disclose a memory because free memory is released.

If you have to apply for memory with a pointer parameter, you should use the "pointer to the pointer", see Examples 7-4-2.

Void getMemory2 (char ** p, int num) {* p = (char *) malloc (sizeof (char) * num);

}

Void test2 (void) {char * str = null; getMemory2 (& STR, 100); // Note Parameter is & Str, not Str structure (STR, "Hello"); cout << str << endl; free (STR) }

Example 7-4-2 Apply Dynamic Memory with a pointer to a pointer

Due to the concept of "pointers" pointing pointers ", we can use function return values ​​to pass dynamic memory. This method is more simple, see Examples 7-4-3.

Char * getMemory3 (int Num) {char * p = (char *) malloc (sizeof (char) * num); Return P;

}

Void test3 (void) {char * str = null; str = getMemory3 (100); strcpy (str, "hello"); cout << str << endl; free (str);}

Example 7-4-3 Return value with a function to pass dynamic memory

This method is transmitted with a function return value to transmit dynamic memory. Although it is easy to use, someone often uses the returnite sentence. Here, it emphasizes not to return to the "stack memory" pointer to the "stack memory", because the internal existence of the function is automatically done, see Examples 7-4-4.

Char * getString (void) {char p [] = "Hello World"; returnof, // The compiler will warn

}

Void test4 (void) {

CHAR * STR = NULL;

Str = getString (); // STR content is garbage

COUT << str << endl;

}

Example 7-4-4 RETURN statement Returns a pointer to "Stack Ins"

Test4 with the debugger, find the Str = getString statement, Str is no longer NULL pointer, but the content of the STR is not "Hello World" but also garbage. If you rewrite the sample 7-4-4, what is it?

Char * getString2 (void) {char * p = "hello world"; returnof

}

Void test5 (void) {char * str = null; str = getString2 (); cout << str << endl;

}

Example 7-4-5 Return statement Returns a constant string

Although the function test5 is running will not be wrong, the design concept of the function getString2 is wrong. Because "Hello World" in getString2 is a constant string, located in a static storage area, it is constant in the program life period. Whenever you call getString2, it returns to the same "read-only" memory block.

7.5 How is Free and Delete how is the pointer?

Don't look at free and delete's name, especially DELETE, they just release the memory fingered by the pointer, but did not kill the pointer itself.

Tracking Examples 7-5 with the Adjustment Piece P is still unchanged (non-null) after the pointer P is still unchanged (non-null), but the memory corresponding to the address is garbage, and P is "wild pointer". If the P is not set to NULL at this time, it will make it mistaken to be a legitimate pointer.

If the program is relatively long, we sometimes remember whether the memory refers to the memory has been released. Before continuing to use P, it usually uses the statement if (p! = Null) to prevent the wrong error. Unfortunately, at this time, if the IF statement does not cause the wrong mistake, because even if the P is not a NULL pointer, it does not point to the legal memory block.

Char * p = (char *) malloc (100); strcpy (p, "hello"); free (p); // P The memory is released, but the address referred to in P is still constant ... if (p ! = Null) // Does not play the wrong resistance {struct (p, "world"); // error}

Example 7-5 P became a wild pointer

7.6 Does the dynamic memory are released automatically?

The local variables in the function are automatically done at the end of the function. Many people mistakenly consider examples 7-6 are correct. The reason is that P is partial pointer variable, which will make the dynamic memory it finishes completely. This is an illusion!

Void Func (Void)

{

Char * p = (char *) Malloc (100); // Does the dynamic memory are released automatically?

}

Example 7-6 Trying to let dynamic memory automatically release

We find that there are some "like," characteristics:

(1) The pointer has done, and does not mean that the memory it is referred to will be released automatically. (2) The memory is released, and does not mean that the pointer will die or become a NULL pointer.

This suggests that the release of memory is not a thing that can be treated. Maybe someone is not convinced, be sure to find out the reason that can be active:

If the program terminates the run, all pointers will die, and the dynamic memory will be recycled by the operating system. In this case, you will not have to release the memory before the program, you don't have to set the pointer to null. Can you be lazy without a mistake?

I want to be beautiful. What if someone else takes the program?

7.7 Eliminate "wild pointer"

"Wild pointer" is not a NULL pointer, which is a pointer to "garbage" memory. People generally don't miss the NULL pointer because it is easy to judge with the IF statement. But "wild pointer" is very dangerous, if the IF statement does not work. There are two main cations of "wild pointers":

(1) The pointer variable is not initialized. Any pointer variable is not automatically become a NULL pointer when it is created, and its default is random, it will finish it. So, the pointer variable should be initialized while creating, or the pointer is set to null, or it points to the legitimate memory. E.g

Char * p = null; char * str = (char *) malloc (100);

(2) The pointer P is after free or delete, it is not set to NULL, making it mistaken to P is a legal pointer. See Section 7.5.

(3) The pointer operation exceeds the scope of the variable. This situation makes people defense, the sample program is as follows:

Class a {public:

Void func (void) {cout << "FUNC OF Class A" << endl;}

}; void test (void)

{

A * p;

{

A a a;

P = & a; // Note A life

}

P-> func (); // P is "wild pointer"}

Function TEST When performing statement P-> FUNC (), the object A has disappeared, and P is pointing A, so P is "wild pointer". But strange is that I have no error when I run this program, which may be related to the compiler.

7.8 Have a malloc / free still need new / delete?

Malloc and Free are standard library functions in C / C language, New / Delete is an operator of C . They can be used to apply for dynamic memory and release memory.

For objects for non-internal data types, light MALOC / FREE cannot meet the requirements of dynamic objects. Objects are created automatically execute constructor automatically, and the object is automatically executed before erophan. Since Malloc / Free is a library function instead of an operator, it is not possible to impose the task of performing constructor and the destructuring function within Malloc / Free.

Therefore, the C language requires a operator New, which can complete the dynamic memory allocation and initialization, and an operator DELETE that can complete the cleaning and release of memory. Note NEW / Delete is not a library function.

Let's take a look at how Malloc / Free and New / Delete implement the dynamic memory management of the object, see Examples 7-8.

Class Obj {public:

Obj (void) {cout << "initialization" << endl;}

~ Obj (void) {cout << "destroy" << endl;

Void Initialize (void) {cout << "initialization" << endl;

Void destroy (void) {cout << "destroy" << endl;}

}

Void UseMalloccFree (void) {Obj * a = (obj *) malloc (sizeof (obj)); // Apply Dynamic Memory A-> Initialize (); // Initialization

// ...

A-> Destroy (); // Clear Work Free (a); // Release Memory

}

Void.com {OBJ * a = new obj; // Apply for dynamic memory and initialize

// ...

Delete a; // Clear and release memory}

Example 7-8 How to Implement Dynamic Memory Management with Malloc / Free and New / Delete

The function initialize of class OBJ simulates the functionality of the constructor, and the function Destroy simulates the functionality of the destructor. Functions UsemallocFree, Since Malloc / Free cannot perform constructor and destructive functions, you must call member functions initialize and destroy to complete initialization and clearing. The function Usenewdelete is much simpler.

So we don't try to use Malloc / Free to complete the memory management of dynamic objects, you should use new / delete. Due to the internal data type "Object" does not construct and destructure process, Malloc / free and new / delete are equivalent.

Since New / Delete's function completely covers Malloc / Free, why does C do not eliminate Malloc / Free? This is because C programs often want to call C functions, and the C program can only manage dynamic memory with malloc / free. If you release "New Create Dynamic Object" with Free, the object may cause a program error because you cannot perform a destructor. If you release "Malloc Application Dynamic Memory" with Delete, theoretical procedure will not be wrong, but the readability of the program is very poor. So new / delete must be paired, the Malloc / Free is the same.

7.9 What should I do if the memory consumption?

If you find a large bundle block, Malloc and New will return NULL pointers when you apply for dynamic memory, and the memory request failed. There are usually three ways to process "memory consumption" issues.

(1) The judgment is NULL if it is, if so, use the RETURN statement to terminate this function. E.g:

Void func (void) {a * a = new a; if (a == null) {return;}

...

}

(2) Decision is NULL if it is immediately, if it is immediately terminated. E.g:

Void func (void) {a * a = new a; if (a == null) {cout << "Memory Exhausted" << Endl; exit (1);} ...

}

(3) Set an exception handler for NEW and Malloc. For example, Visual C can use the _SET_NEW_HANDER function to set the user's own exception handler to NEW, or the Malloc has the same exception handler as the New. For details, please refer to the C manual.

The most common in the above (1) (2) is used. If there is a need to apply for dynamic memory in a function, then the way (1) will not be able to handle it from the heart (release memory is very troublesome).

Many people can't bear to use exit (1), ask: "Do not write an error handler, let the operating system solve it?"

Not. If you happen something such a "memory consumption", it is generally not necessary to save. If you don't use EXIT (1) to kill bad programs, it may kill the operating system. The truth is like: If you don't kill the gangsters, the gangsters will commit more sins before they die.

There is a very important phenomenon to tell everyone. For more than 32-bit applications, it is almost impossible to cause "memory consumption" whether using Malloc and New. I wrote the test program with Visual C in Windows 98, see Examples 7-9. This program will run without rest and will not terminate. Because the 32-bit operating system supports "virtual memory", the memory is used, and the hard disk space is automatically used. I only heard the hard drive, and Window 98 has been tired of keyboard, mouse without reacting. I can give such a conclusion: For more than 32-bit applications, the "memory consumption" error handler is useless. This will be uniforms with the Unix and Windows programmers: Anyway, the error handler does not work, I will not write, save a lot of trouble.

I don't want to mislead readers, you must emphasize: Do not add error handling will result in a poor quality of the program, never done due to small loss.

Void main (void) {float * p = null; while (true) {p = new float [1000000];

COUT << "Eat Memory" << endl;

IF (p == null) exit (1);}

}

Example 7-9 Trying to deplete the memory of the operating system

7.10 Malloc / Free

The prototype of the function malloc is as follows: void * malloc (size_t size); Apply a length of the length of the integer type with a malloc, the program is as follows: int * p = (int *) malloc (sizeof (int) * length); we should Concentrate on two elements: "Type Conversion" and "SizeOf".

The type of Malloc returns is VOID *, so it is necessary to explicitly turn the type conversion when calling Malloc, converts VOID * to the required pointer type. The Malloc function itself does not identify what type of memory to be applied, it only cares about the total number of bytes of memory. We can never remember int, Float and other data types of variables of the exact byte number. For example, the int variable is 2 bytes under the 16-bit system, and below 32 is 4 bytes; and the float variable is 4 bytes under the 16-bit system, and below 32 is 4 bytes. It is best to use the following procedure to test:

Cout << sizeof (char) << Endl;

COUT << sizeof (int) << ENDL;

COUT << sizeof (unsigned int) << ENDL;

Cout << sizeof (long) << Endl;

COUT << SizeOf (unsigned long) << endl;

Cout << sizeof (float) << Endl;

Cout << sizeof (double) << Endl;

Cout << sizeof (void *) << Endl; in Malloc "()" using the Sizeof operator is a good style, but should be careful, we will faint heads, write p = malloc (Sizeof (P)) Such a program is coming.

The prototype of the function free is as follows:

Void Free (Void * MEMBLOCK);

Why is the free function is not as complicated as the malloc function? This is because the type of pointer P and the capacity of the memory it refers to in advance, and the statement free (p) can release memory correctly. If P is a null pointer, Free will not have problems with the operation of P no matter how many operations. If the P is not a NULL pointer, the FREE will cause the program to run errors twice. 7.11 New / delete's use points

The operator NEW is much more simpler than the function Malloc, for example:

INT * p1 = (int *) Malloc (intend * length);

INT * p2 = new int [length];

This is because NEW has built-in SIZEOF, type conversion, and type security check functions. For objects for non-internal data types, New completed initialization work while creating dynamic objects. If the object has multiple constructor, then the NEW statement can also have a variety of forms. E.g

Class Obj {public: obj (void); // None parameter constructor OBJ (int x); // With a constructor of a parameter

...

} void test (void) {Obj * a = new obj; obj * b = new obj (1); // initial value is 1

...

delete a; delete b;}

If you create an object array with NEW, you can only use the parameter constructor of the object. For example, Obj * Objects = new obj [100]; // Creating 100 dynamic objects cannot be written as OBJ * Objects = new obj [100] (1); // Creating 100 dynamic objects while assigning initial value 1 Release with Delete When an object array, pay attention not to the symbol '[]'. For example, DELETE [] Objects; // The correct usage delete objects; // The latter is equivalent to delete objects [0], and another 99 objects are missing.

7.12 Some experience

I know a lot of technical Nice C / C programmers, very few people can pat the chest and say the pointer and memory management (including myself). I first learned the C language, I was particularly afraid of the pointer, causing me to develop the first application (about 10,000 line C code) did not use a pointer, and the full-use array came to the top replacement pointer, which is stupid. Sudok the pointer is not a way, and later I have rewritten this software, and the code amount is reduced to half of the original.

My lesson is:

(1) The more fear of the pointer, the more you need to use the pointer. It doesn't use the pointer correctly, and it is definitely not a qualified programmer.

(2) The habit of "using the debugger step-by-step tracking program", only this can find the nature of the problem.

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 Function Overloaded Concept 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 (...); // 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. For example: void function (void); int function (Void); the above two functions, the first no return value, the second return value is an int type. If this call function is called: int x = function (); then 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. E.g:

EXTERN "C"

{

Void foo (int x, int y);

... // Other functions

} Or write

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. For example: 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 a member function Print, the global function should be added to add & # 0; :: & # 0; mark. Such as: print (...); // means print is a global function rather than member functions 8.1.3 When the implicit type conversion causes the overload function to generate an error example 8-1-3, the first OUTPUT function parameters Is int type, the parameter of the second OUTPUT function is a float type. 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.

# c < void output (int x); // Function declares 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 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 overlay member functions are overloaded: (1) The same range (in the same class); (2) The function name is the same; (3) Virtual keyword None. The overlay is the specifier of the class function overlay base class function. The features are: (1) Different ranges (located in the derived class and base class); (2) The function name is the same; (3) The parameter must be There are virtual keywords. 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 deact: 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 Improved hidden rules This is not difficult to distinguish between overload and coverage, but C hidden rules have caused problem complexity to increase. Here "hidden" is a function that is a function of generateing a bobbin. The rules are as follows: (1) If the derived class function is the same name with the base class, 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) Overwrite 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 derived; pd-> f (10); // error}

Example 8-2-3 Errors from example 8-2-3 due to hidden, hidden rules seem to be stupid. But there are at least two reasons for hidden rules:

The person who writes statement PD-> F (10) may really want to call the Derived :: F (char *) function, but he mistakenly writes the parameters. 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. 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:

[Rules 8-3-1] Parameter default values ​​can only appear in a statement of functions, and cannot appear in the definition body. For example: void foo (int x = 0, int y = 0); // correct, default value appears in the declaration of the function, Void foo (int x = 0, int y = 0) // error, the default value appears Why is this in the definition body of a function? 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.

[Rule 8-3-2] If the function has multiple parameters, the parameters can only be deficed from the back, otherwise the function calls statement blame will be caused. 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 will result in an emphasis on the overload 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 operator overload to represent: Complex Operator (Const Complex & A, Const Complex & B); Different Operators Different from ordinary functions during call The point is: For normal functions, the parameters appear in parentheses; for operators, parameters appear on their left and right. For example, COMPLEX A, B, C; ... C = add (a, b); // with normal function c = a b; // with operator if operator is overloaded as global function, there is only one parameter The operator is called a yuan operator, and the operator of two parameters is 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 Rules All One Dollar Operators Recommended to Member Functions = () [] -> Only Overloaded as Member Function = - = / = >> = | =% = >> = << = It is recommended to overworld the member functions to be 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 operators with ordinary hearts: (1) Don't worry too much, it is not used, 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 are 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) Can't overload & # 0;. & # 0; because & # 0;. & # 0; meaningful for any member in the class, has become a 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 Used Macro code with inner

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. For example #define max (a, b) (a)> (b)? (A): (b) Result = max (i, j) 2; the pre-regulator is explained as Result = (i)> J)? (i): (j) 2; due to operators & # 0; & # 0; than operator & # 0;: & # 0; high priority, the above statement does not equivalent Expected Result = ((i)> (j)? (I): (j)) 2; if the macro code is rewritten as #define max (a, b) ((a)> (b)? (A) : (b)) You can resolve errors caused by priority. However, even if the macro code after using the modified macro code is not very unhappy, such as statement Result = max (i , j); explain the pre-regulator to Result = (i )> (j)? (I ): (j); C , there is another shortcoming of using 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 The programming style of the inline function The keyword inline must be placed with the 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 only with the function declaration together Void foo (int x, int y) {...} and the following style function foo Then, the inline function: Void foo (int x, int y); inline void foo (int x, int y) // inline is set together with function definition {...} So, inline is a kind of "for implementation Keywords ", 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 become inline functions, such as class a {public: void foo (int x, int y) {...} // automatically becomes inline function} Defining members of the member function Although it can bring convenience on the class declaration, it is not a good programming style. The above case should be changed to: // header file Class a {public: void foo (int x, int y);} // Definition file inline void a :: foo (int x, int y) {...

} 8.5.3 Cautious use inline inline can improve the performance efficiency of the function, why not define all functions as 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 inline: (1) If the code in the function body is relatively long, use the inline will result in a 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.

Chapter 9 Constructor, Destructor and Assignment Function

The constructor, the destructor and assignment function are the most basic functions of each class. They are too ordinary, which makes it easy to paralyze, in fact, these seemingly simple functions are dangerous as there is no top water. Each class has only one destructuring function and an assignment function, but there can be multiple constructor (including a copy constructor, others referred to as a normal constructor). For any class A, if you don't want to write the above functions, the C compiler will automatically generate four default functions, such as

A (void); // Default parameter constructor A (const A & a); // default copy constructor ~ a (void); // Default destructor A & Operate = (Const A & a); // The default assignment function This is not forbidden, since it can automatically generate functions, why do you still have programmers? The reason is as follows:

(1) If the "default non-parameter constructor" and "default destructor" are used, it is good for the C inventor Stroustrup to abandon the opportunity of independent "initialization" and "cleaning".

(2) The "default copy constructor" and "default assignment function" are implemented in a "bit copy" rather than "value copy", if the class contains pointer variables, these two functions are destined to be wrong .

For C programmers who don't have a bitter, if he says writes constructor, the destructor and assignment function is easy, you can not use your brains, indicating that his understanding is more superficial, and the level needs to be improved.

In this chapter, in the case of the design and implementation of class String as an example, the in-depth explanation is neglected by many textbooks. The structure of the String is as follows:

Class string {public: string (const char * str = null); // Ordinary constructor string (const string & other); // copy constructor ~ string (void); // Destructure function string & operate = (const string & Other); // Assign value private: char * m_data; // Used to save string};

9.1 Structure function and secting function

As a more advanced language than C, C provides a better mechanism to enhance the security of the program. The C compiler has a strict type security check function, which can almost find out all the syntax issues in the program, which has made the programmer's busy. However, the program has passed the compilation check and does not mean that the error does not exist. In the "wrong" big family, the status of "syntax error" can only be a little brother. High-level mistakes are usually hidden, just like a criminal, you can't easily catch him.

Based on experience, many difficult program errors are due to the correct initialization or removal of variables, while initialization and clearing work are easily forgotten. Stroustrup takes into account this problem when designing C languages ​​and resolves well: put the object's initialization work in the constructor, put the clear work in the destructor. When the object is created, the constructor is executed automatically. When the object is dying, the destructor is automatically executed. There is no need to worry about forgetting the initialization and clearance of the object.

The name of the constructor and the destructor cannot be casually, and the compiler can be made automatically. The naming method of Stroustrup is simple and reasonable: the constructor, the destructor function, the same name, due to the opposite of the destructor, and the contrary to the constructor, the prefixed '~' is shown differently.

In addition to the name, the other specialty of the constructor and the destructuring function is not the return value type, which is different from the function of the return value type Void. The mission of constructor and the descent function is very clear, just like being born with death, light slippery. If they have a return value type, the compiler will not know what it is. In order to prevent the export branch, simply specify that there is no return value type. (In the above, the presentation of the "EEKEL, P55-P56]) 9.2 Construction Function

The constructor has a special initialization mode called "Initialization Expression Table" (referred to as initialization table). After the initialization table is behind the function parameter table, it is before the function body {}. This shows that the initialization work in the table occurs before any code in the function body.

Use rules for constructor initialization table:

If there is a inheritance relationship, the derived class must call the constructor of the base class in its initialization table.

E.g

Class A

{...

A (int X); // a constructor}; Class B: Public a

{...

B (int x, int y); // b constructor}; b :: b (int x, int y): a (x) // Call a constructor in the initialization table {

...

}

The CONST constant of the class can only be initialized in the initialization table because it cannot be initialized in a function body (see Section 5.4). The initialization of the data member of the class can be used in both initialization tables or functions in the function, and the efficiency of both ways is not exactly the same.

Members of non-internal data types should be initialized in the first way to obtain higher efficiency. For example, Class A

{... a (void); // No parameter constructor A (Const A & Other); // Copy Construction Function A & Operate = (Const A & Other); // Assignment Function};

Class B {public: B (Const A & a); // B Construction Function Private: a m_a; // Member Object}; Example 9-2 (a), the constructor of class B is called in its initialization table. The copy constructor of class A will initialize the member object M_A. In Example 9-2 (b), the constructor of class B initializes member objects M_A in the function body. What we see is just a assignment statement, but actually B constructor dry two things: firstly create M_A objects (call the parameter constructor of A), then call the assignment function of class A, to assign the parameter A Give M_A.

B :: B (Const A & a): M_a (a) {...} B :: B (Const A & a) {

m_a = a;

...

}

Example 9-2 (a) Member Object Inly in Initialization Table Example 9-2 (b) member object is initialized in the function body

For data members of internal data types, the efficiency of both initialization methods has little difference, but the latter's program seems to be clearer. If the declaration of class f is as follows: Class f {public: f (int X, int y); // Constructor private: int m_x, m_y; int m_i, m_j;} example 9-2 (c) in F The first initialization method is used, and the constructor of F in Example 9-2 (d) is used in the second initialization method.

F :: f (int x, int y): m_x (x), m_y (y) {m_i = 0; m_j = 0;} f :: f (int x, int y) {m_x = x; m_y = y ; m_i = 0; m_j = 0;}

Example 9-2 (c) Data Members are initialized in the initialization table, and the data member is initialized in the function body.

9.3 Senching of constructors and sectations

The construct begins with the most root of the class level. In each layer, the constructor of the base class is first called, and then the constructor of the member object is called. The sequence is performed in strict accordance with the instead of constructing, and the order is unique, otherwise the compiler will not automatically perform the destructure process. An interesting phenomenon is that the order of the initialization of member objects is completely not affected by their order in the initialization table, only by the order of the member objects declared in the class. This is because the statement of the class is unique, and the class constructor can have multiple, so there will be a plurality of different order initialization tables. If the member object is constructed in the order of the initialization table, this will cause the destructuring function that cannot be unique. [Eckel, p260-261]

9.4 Example: Constructor and destructor of class String

// String ordinary constructor

String :: String (const char * str)

{IF (Str == NULL) {m_data = new char [1]; * m_data = '/ 0';} else {intlength = strlen (str); m_data = new char [length 1]; strcpy (m_data, Str);}}

// String destructor

String :: ~ String (void)

{

delete [] m_data;

// Since M_DATA is the internal data type, it is also possible to write a delete m_data;

}

9.5 Do not underestimate the copy constructor and assignment function

Since the copy constructor and assignment function will be used, the programmer may look at some of these two functions. Please remember the following warnings, you will be careful when reading your text:

This chapter starts, if you do not actively write a copy constructor and assignment function, the compiler will automatically generate default functions in a "bit copy". If the class is included in the pointer variable, the two default functions implies an error. With the two objects A, B, B, B as an example, assuming that A.M_DATA is "Hello", B.M_Data's content is "world".

The "bit copy" that is assigned to b, the "bit copy" of the default assignment function means executing B.M_DATA = A.m_Data. This will cause three errors: First, the original memory of B.m_data is not released, resulting in memory disclosure; second, B.M_Data and A.m_Data points to the same block, A or B changes will affect the other party; three When the object is analyzed, m_data is released twice.

Copy constructor and assignment functions are very easy to confuse, often leading to wrong, misuse. The copy constructor is called when the object is created, and the assignment function can only be called in the already existing object. In the following procedures, the third statement and the fourth statement are very similar. Which one calls the copy constructor, which calls the assignment function?

String a ("Hello");

String B ("World");

String c = a; // Call the copy constructor, it is best to write c (a);

C = b; // Call the assignment function

In this case, the style of the third statement is poor, it is recommended to be String C (a) to distinguish between the fourth statement.

9.6 Example: Copy Construction Function and Assignment Function of Class String

// Copy constructor string :: string (const string&>) {

/ / Allow Other private members M_Data

INT length = strlen (other.m_data);

m_data = new char [length 1];

STRCPY (m_data, other.m_data);

}

// Assignment function string & string :: Operate = (const string&>) {// (1) Check the self-assignment IF (this == & other) return * this; // (2) Release the original memory resources DELETE [] m_data; // (3) Assign new memory resources and copy content int length = Strlen (Other.m_data);

m_data = new char [length 1];

STRCPY (m_data, other.m_data); // (4) Return to the reference Return * this;}

The difference between the class String copy constructor and the ordinary constructor (see Section 9.4) is that there is no need to compare with NULL at the entry, because "reference" cannot be NULL, and "pointer" can be null.

The assignment function of class String is much more complicated than the constructor, and the four steps are implemented:

(1) First step, check the self-assignment. You may think more about it, is it stupid to write a self-assignment statement like A = A! It doesn't. But indirect self-assignment still may appear, for example

// Content self-assignment B = a; ... c = b; ... a = C; // address self-assignment value B = & a; ... a = * b;

Perhaps someone will say: "Even if you have a self-assignment, I can ignore it. I don't pay attention to the time to let the object copy themselves. Anyway, I will not have an error!" He really said wrong. Look at the second step DELETE, can you copy yourself after committing suicide? So, if you find a self-assignment, you should immediately terminate the function. Be careful not to check the IF statement IF (this == & Other) error written as if (* this == Other) (2) second step, release the original memory resources with Delete. If it is not released now, there is no chance in the future, which will cause memory leakage. (3) Step 3, allocate new memory resources, and copy strings. Note that the function strlen returns the valid string length and does not contain end values ​​'/ 0'. The function strcpy is copied together with '/ 0'. (4) The fourth step, returns the reference to this object, and the purpose is to implement chain expression such as A = B = C. Be careful not to write Return * this to RETURN THIS. So can you write RETURN OTHER? Is the effect not the same? No! Because we don't know the lifetime of the parameter Other. It is possible that other is a temporary object, and it immediately disappears after the assignment, then returnit is returned to the garbage.

9.7 Laying method to handle copy constructor and assignment function

If we don't want to write a copy constructor and assignment function, it is not allowed to use the default function generated by the compiler. The lazy way is: just declare the copy constructor and assignment function as a private function, no need to write code. For example: Class A

{...

Private: a (const A & a); // Private copy constructor A & Operate = (const A & a); // Private assignment function}; if someone tries to write the following procedure: A b (a); // call Private copy constructor b = a; // Call the private assignment function compiler will point out errors because the outside world cannot operate A private function.

9.8 How to implement the basic function of the class in the derived class

The construction function, destructuring function, and assignment function of the base class cannot be inherited by the derived class. If there is a inheritance relationship between the class, you should pay attention to the following when writing the above basic functions:

The constructor of the derived class should call the constructor of the base class in its initialization table. The destructor of the base class and the derived class should be the virtual (ie, the Virtual keyword). E.g

#include

Class Base

{

PUBLIC:

Virtual ~ base () {cout << "~ base" << endl;}

}

Class Derived: Public Base

{

PUBLIC:

Virtual ~ derived () {cout << "~ derived" << endl;}

}

Void main (void)

{

Base * pb = new deerived; // upcast

Delete PB;

}

The output is:

~ Derived

~ Base

If the destructor is not imaginary, the output result is

~ Base

When writing the assignment function of the derived class, be careful not to forget the data member of the base class to re-value. E.g:

Class Base {public:

...

Base & Operate = (const base & other); // class base assignment function private: int m_i, m_j, m_k;}; class derived: public base {public: PUBLIC:

...

Derived & Operate = (const derived & other); // class Derived assignment function private: int m_x, m_y, m_z;}; derived & derived :: Operate = (const deive&) {// (1) Check the self-assignment IF (this == & other) Return * this; // (2) Re-assind the data for the base class Base :: OPERATE = (other); // Because the private data member // (3) is not directly Data member assignment m_x = other.m_x; m_y = other.m_y; m_z = other.m_z; // (4) Return to this object Return * this;}

9.9 Some experience

Some C programming books are called constructor, destructor and assignment functions are class "BIG-Three", which is indeed any of the most important functions, which is not despised.

Maybe you think the content of this chapter is enough, learn to be safe, I can't make this guarantee. If you want to eat "Big-Three", please read the reference [cline] [meyers] [murry].

Chapter 10 Inheritance and Combination

Object is an instance of classes (instance). If the object is more than a house, then the class is the design drawings of the house. So the focus of object-oriented design is the design of the class, not the design of the object. For C programs, the design isolated class is relatively easy, and it is difficult to correctly design the base class and its distribution. This chapter only discusses the concept of "inheritance" and "combination".

Note that the application hotspot between the current face-to-object technology is COM and CORBA, which exceeds the category of C textbooks, please read COM and CORBA.

10.1 inheritance

If A is a base class, B is a derived class of A, then b will inherit a data and functions. E.g:

Class A

{

PUBLIC: VOID FUNC1 (Void); Void Func2 (VOID);

Class B: Public A

{

PUBLIC: Void Func3 (Void); Void Func4 (Void);

Main ()

{

B B;

B.Func1 (); // b inherited from a Func1

B.func2 (); // b inherited from a function FUNC2

B.func3 ();

B.func4 ();

}

This simple sample program illustrates a fact that the "inheritance" feature of C can improve the reuse of the program. Because "inheritance" is too useful, it is too easy to use, it should be prevented from using "inheritance". We should give "inheritance" to have some rules.

[Rule 10-1-1] If class A and class B are not associated, it is not possible to let B inherit a function and properties in order to make B function more. Don't think "White White Don't eat", let a good-end healthy youth to eat people to eat people. [Rule 10-1-2] If the "a Kind of) is logically b is A, B is allowed to inherit a function and attributes. For example, a man (MAN) is a kind of human (human), the boy (Boy) is a man. Then the class Man can derive from class Human, and class BOY can derive from class Mana.

Class Human

{

...

}

Class Man: Public Human

{

...

}

Class Boy: Public Man

{

...

}

Precautions

[Rule 10-1-2] It looks very simple, but there may be unexpected applications when practical applications, the concept of inheritance is not exactly the same in the program world and the real world.

For example, from a biological point of view, an ostrich is one of the birds (BIRD). It is reasonable that ostrich should be derived from class BIRD. But ostrich can't fly, then ostrich :: fly is something?

Class Bird {public: Virtual void fly (void); ...}; Class Ostrich: Public Bird {...};

For example, from a mathematical perspective, a circle is a special ellipse, which is reasonable that Circle should be derived from class Ellipse. However, the ellipse has a long axis and a short axis. If the circle inherits the long axis and the short axis of the ellipse, do you not draw a snake?

Therefore, more stringent inheritance rules should be: If it is logically b is a "one", all the features and attributes of A are meaningful to B, B is allowed to inherit a function and attributes.

10.2 combination

[Rule 10-2-1] If the logical A is a "part of a part", B is not allowed to derive from A, but to combine B with A and other things. For example, Eye, Nose, Mouth, Ear (EAR) is part of the head (HEAD), and the class HEAD should be combined by Eye, Nose, Mouth, EAR, not derived. As shown in Example 10-2-1.

Class eye {public: void look;}; class nose {public: void smell (void);}; class mouth {public: void eat (void);}; class ear {public: Void Listen (Void); }; // The correct design, although the code is lengthy. Class head {public: Void Look (void) {m_eye.look (); void Smell (void) {m_nose.smell ();} void eat (void) {mouth.eat ();} void listen (void) { M_ear.listen ();} private: eye m_eye; nose m_nose; mouth m_mouth; ear m_ear;};

Example 10-2-1 Head is combined by Eye, Nose, Mouth, EAR

If Head is allowed from Eye, Nose, Mouth, EAR, then Head will automatically have these features of Look, SMELL, EAT, LISTEN. Examples 10-2-2 are very short and operate correctly, but this design method is wrong.

// The function is correct and the code is simple, but the design method is wrong. Class Head: Public Nose, Public Nose, Public Mouth, Public Ear {};

Example 10-2-2 Head derived from Eye, Nose, Mouth, EAR

A rooster pursued a hen who just got the egg, do you know why? Because the hens underwent the duck egg. Many programmers have committed the design mistakes to solve the temptation of "inheritance". "Run the correct" program is not necessarily a high quality program, which is an example.

Chapter 11 Other Programming Experience 11.1 Using const to improve the robustness of functions

Seeing the const keyword, the C programmer first thinks of the constant constant. This is not a good condition reflection. If you only know the constant defined with const, it is equivalent to only use gunpowder to make firecrackers. Const greater charm is that it can modify the parameters, return values, or even function of the function.

Const is Constant's abbreviation, "constant" means. Things that are modified by const are mandatory, prevent accidents, can improve the robustness of the program. So many C programming books Suggestions: "Use constly you need".

11.1.1 Parameters with const modifying functions If the parameter is output, no matter what the data type is used, whether it adopts "pointer" or "reference delivery", you cannot add const modification, otherwise the parameter will lose the output function. Const can only modify the input parameters:

If the input parameter is adopted "Pointer Pass", then CONST modification prevents accidentally change the pointer to protect the protection. For example, StringCopy functions: void stringcopy, const char * strsource; where strsource is an input parameter, strDestination is the output parameter. After adding CONST modification, if the statement in the function is trying to change the content of strsource, the compiler will indicate an error. If the input parameter is "value passed", since the function will automatically generate temporary variables for copying this parameter, the input parameters do not need to be protected, so do not add const. For example, don't write the function void func1 (int x) as a VOID FUNC1 (Const Int X). Similarly, do not write the function void func2 (a a) as Void Func2 (Const A A). Where A is a user-defined data type.

For the parameters of the non-internal data type, the function is destined by the VOID FUNC (A a). Because the functionality of the A type of temporary objects will be used to copy parameter A, and the temporary object constructs, copying, and secting processes will consume time. In order to improve efficiency, the function declaration can be changed to Void Func (A & A), because "reference delivery" only borrows the alias of the parameter, there is no need to generate temporary objects. However, the function Void Func (A & A) has a disadvantage: "Reference Pass" is likely to change the parameter a, which is our undesirable. Solving this problem is easy, add const modification, so the function will eventually become Void Func (Const A & A). In this kind, whether Void Func (INT X) should be rewritten as Void Func (Const Int & X) to increase efficiency? There is no need, because the parameters of the internal data type do not have construction, the process of sect, and the replication is very fast, "value transfer" and "reference delivery" are almost fairly. The problem is so lingering, I have to summarize the use of "const &" modified input parameters, as shown in Table 11-1-1.

For input parameters for non-internal data types, "value delivery" should be changed to "const reference", and the purpose is to improve efficiency. For example, Void Func (a a) is changed to Void Func (Const A & A). For input parameters for internal data types, do not change the "value to" to "Const reference". Otherwise, it will not only improve the efficiency, and the understandability of the function is reduced. For example, Void Func (INT X) should not be changed to Void Func (Const Int & X).

Table 11-1-1 "Const &" Rules for Modifying Input Parameters 11.1.2 Return Value with const modified functions

If the value is returned to a "pointer" mode, the content returns (ie, the pointer) can not be modified, and the return value can only be assigned to the same type of CONST modified. For example, a function const char * getString (void); the following statement will appear error: char * str = getString (); the correct usage is const char * str = getString ();

If the function return value uses "value transfer mode", since the function is copied to the external temporary storage unit, there is no value. For example, don't write the function INT GetInt (Void) into const Int GetInet (VOID). Do not write the function a geta (void) as a CONST A GETA (Void), which is a user-defined data type. If the return value is not an internal data type, the function A geta (void) is indeed increasing efficiency. But it is necessary to be careful at this time, be sure to figure out the "copy" of the function wants to return an object or returns "alias", otherwise the program will be wrong. See Section 6.2 "Return Value Rules". The function return value is not much in the case of "reference delivery", which typically only appears in the assignment function of the class, the purpose is to achieve chain expression. For example, Class A {... a & operate = (const A & other); // assignment function}; A, B, C; // a, b, c is a target of A. A = B = C; // Normal Chain assignment (a = b) = C; // is not normal chain assignment, but if the return value will be modified if the return value of the assignment function is added, the content of the return value is not allowed to be changed. In the above example, statement a = b = c is still correct, but the statement (a = b) = c is illegal. 11.1.3 Const Member Functions Any function that does not modify data members should be declared as a const type. If you write a Const member function, you accidentally modify the data member, or call other non-Const member functions, the compiler will point out errors, which will undoubtedly improve the robustness of the program.

In the following procedure, the member function getCount of class stack is only used to count, and the getCount should be a const function from logically. The compiler will point out errors in the getCount function.

Class stack {public: void push; int pop (void); int getCount (void) const; // constant member function private: int m_num; int m_data [100];}; int stck :: getCount (Void ) const { m_num; // Compiling errors, try to modify data members m_num pop (); // Compiling errors, trying to call non-const function return m_num;} The declaration of the Const member function looks whimed: const keyword It can be placed on the tail of the function declaration, which is probably because it has been occupied elsewhere.

11.2 Improve the efficiency of the program

The time efficiency of the program refers to the speed of operation, and the spatial efficiency is the condition of the program occupies memory or existence. The global efficiency refers to the efficiency of standing throughout the system, and local efficiency refers to the efficiency considered at the point of view of the module or function.

[Rule 11-2-1] Do not pursue the efficiency of the procedure, should improve the efficiency of the program under the premise of satisfying the correctness, reliability, robustness, readability and other quality factors.

[Rule 11-2-2] is mainly based on the global efficiency of the program, and the local efficiency is supplemented.

[Rule 11-2-3] When optimizing the efficiency of the procedure, the "bottleneck" of the restriction efficiency should be found, do not optimize insignificant.

[Rule 11-2-4] First optimize the data structure and algorithm, and then optimize the execution code.

[Rules 11-2-5] Sometimes time efficiency and spatial efficiency may be opposite, at this time, it should be analyzed and appropriate compromise. For example, spend some memory to improve performance.

[Rule 11-2-6] Do not pursue compact code, because compact code does not produce efficient machine code.

11.3 Some beneficial advice

[Recommendation 11-3-1] Be careful to write errors for operators that are not easy to distinguish. We often write "==" to "=", like "||", "&&", "<=", ">", is also very prone to "loss 1" mistakes. However, the compiler does not necessarily automatically point out such an error.

[Recommendation 11-3-2] Variables (pointers, arrays) should be initialized after being created to prevent the variables that are not initialized from being used as right values.

[Recommendation 11-3-3] When the initial value of the core variable, the default value is wrong, or the accuracy is not enough.

[Recommendation 11-3-4] Beware of the incorrect transition of data type. Try to use explicit data type conversion (let people know what happened), avoid making the compiler slidably implicitly converted.

[Recommendation 11-3-5] Beware of overflow or underflow, and the subscript of the array.

[Recommendation 11-3-6] Be careful to write the wrong handler, when the heart error handler itself is incorrect.

[Recommendation 11-3-7] Beware of the file I / O.

[Recommendation 11-3-8] Avoid writing skills high code.

[Recommendation 11-3-9] Do not design, very flexible data structure.

[Recommendation 11-3-10] If the original code quality is better, try to be multiplexed. But don't patch a very bad code, you should rewrite.

[Recommendation 11-3-11] Try to use the standard library function, do not "invent" existing library functions.

[Recommendation 11-3-12] Try not to use a variable that is closely related to specific hardware or software environment.

[Recommendation 11-3-13] Set the selection of the compiler to the most stringent state.

[Recommendation 11-3-14] If possible, use PC-LINT, LOGISCOPE and other tools for code review.

references

[CLINE] MARSHALL P. CLINE AND GREG A. Lomow, C FAQs, Addison-Wesley, 1995

[Eckel] Bruce Eckel, Thinking In C (C Programming Thought, Liu Zongtian and other translation), Machinery Industry Press, 2000

[MAGUIRE] Steve Maguire, Writing Clean Code (Programming Essence, Jiang Jingbo et al) Electronic Industry Press, 1993

[Meyers] Scott Meyers, Effective C , Addison-Wesley, 1992

[Murry] Robert B. Murry, C Strategies and Tactics, Addison-Wesley, 1993

[Summit] Steve Summit, C Programming Faqs, Addison-Wesley, 1996

Appendix A: C / C Code Review Form

Document Structure Importance Review Item Conclusion The name of the header file and the definition file reasonable? Is the directory structure of the header file and the definition file reasonable? Whether the copyright and version declaration is complete? Does the important header file use the IFNDEF / DEFINE / ENDIF preparation block? Is it only stored in the "declaration" in the header file without storing the "definition" ... The typographical importance review item of the program conclusively, is there a gap? Is the space in the code line be decent? Is the long line split? Does "{" and "}" each occupied and aligned with the same column? Is an important line of code only do one thing? If you only define a variable, you only write a statement. Important if, for, while, do, other words, whether "{}" is added regardless of how much the execution statement is executed. Is it important when defining a variable (or parameter), is it modified? Whether the comment is clear and necessary? Is important notes have errors or maybe misunderstand? Does important classes of public, protected, and private sequences that are consistent in all programs? .. Is the identifier intuitive and can you spell? The length of the identifier should comply with the principle of "min-length &&max-information? Does the same local variables and all variables in important procedures? Are the class names, function names, variables, and parameters, whether constant write formats follow certain rules? Static variables, global variables, whether the members variable of the class add prefix? ...... Expression and basic statement Importance Review Issue Conclusion Important If the operator in the code line is more, whether it has been clearly determined by the brackets to clearly determine the order of operation? Whether to write too complicated or multi-purpose composite expressions? Is it important to confuse composite expressions with "real mathematical expressions"? Important whether to write if statements with implicit errors? For example, (1) compares the Boolean variable to TRUE, FALSE or 1, 0. (2) Compare floating point variables with "==" or "! =" And any number. (3) Compare the pointer variable "==" or "! =" And NULL. If there is a logic judgment in the circulation, the number of cycles is large, whether the logic has been changed to the outside of the cyclic body? Does the important case statement end? Do you forget to add BREAK? Important whether to forget the default branch of Switch? Do you have a hidden danger when using the GOTO statement? For example, skip the construction of some objects, the initialization of variables, important calculations, etc. ...... Conclusion of the Importance Review Item Conclusion Use a meanled intuitive constant to indicate those numbers or strings that will appear multiple times in the program? In the C program, is it replaced with a CONST constant? Important If a constant is closely related to other constants, is this relationship included in the definition? Do you misunderstand the CONST data member in the class? Because const data members are constants only within an object survival, they are variable for the entire class. ...... Is the writing of the importance of the function design The writing of the parameters is complete? Don't be greedy, you will omit the name of the parameter.

Is the parameter named, is the order reasonable? Is the number of parameters too much? Is the type and number of uncertain parameters? Whether the type of function return value is omitted? Is the function name and the return value type conflict in semantics? Is important return of normal values ​​and error flags? The normal value should be obtained with the output parameters, and the error flag returns with the return statement. Important in the "entrance" of the function body, is it checking the validity of the parameters with assert? Important use abuse of Assert? For example, confusion illegal situations and errors, the latter inevitably exist and must be processed. The important returnite statement returns "pointer" or "reference" to "Stack Ins"? Do you use const to improve the robustness of the function? Const can force the definition of the parameters, return values, and even functions of the function. "Uses Const WHENEVER You NEED" ... Memory Management Importance Review Item Conclusion It is important to use Malloc or New to apply for memory immediately check whether the pointer value is NULL? (Preventing memory from using a pointer value null) Is it important to forget the initial value of the array and the dynamic memory? (Preventing memory that will not be initialized as the right value) Is the key group or the subscript of the pointer? Is the application and release of important moving memory? (Preventing the Memory Leakage) Is important to effectively handle the "memory consumption" problem? Is important to modify the content of "pointer to constant"? Is it important to appear wild pointer? For example, (1) The pointer variable is not initialized. (2) After using Free or Delete released the memory, forget to set the pointer to NULL. Is important confusion of Malloc / Free and New / DELETE? Is the important malloc statement correct? For example, is the number of bytes correct? Is the type conversion correct? Important When creating and releaseing a dynamic object array, is the new / delete statement correct? ...... Advanced Features of the C function Importance Review item Conclusion Is there a second meaning? Is it important to confuse the overloading of the member function, override and hide? Is the operator's overload compliant? Is it abuse inline function? For example, the code in the function is relatively long, and a loop is in the function. Important whether to replace the macro code with the inner function? .. (2) The default copy constructor; (3) default destructor; (4) default assignment function. Do you miss some initialization work in important constructor? Is important to use the initialization table of constructor correctly? Is some clear job in the important destructor? Whether it is wrong, miss the copy constructor and assignment function? Important assignment function is generally divided into four steps: (1) Check the self-assignment; (2) Release the original memory resource; (3) Assign new memory resources, and copy content; (4) Return * this.

Will I miss important steps? Important whether the derived class constructor, destructive function, assignment function are written correctly? Note: (1) Detective class is not possible to inherit the constructor of the base class, the destructive function, and assignment function. (2) The constructor of the derived class should call the constructor of the base class in its initialization table. (3) The analyte function of the base class and the derived class should be a virtual (ie, the Virtual keyword). (4) When writing the assignment function of the derived class, be careful not to forget to re-value the data member of the base class. ... (1) If the "one" of A is logically b is a "one", all the functions and attributes of A are meaningful to B, B is allowed to inherit a function and attributes of A. (2) If the logical A is a "part of a part of), B is not allowed from A from A, but to combine B using A and other things. ...... Other Frequently Asked Questions Importance Review Issues Conclusion Important Data Types Questions: (1) Is the data type of variables error? (2) Is there a value of different data types? (3) Is there a comparison of different data types? Important variable value: (1) Initialization or default value of variables? (2) Is the variable overflow or overflow? (3) Is the value of the variable enough? Important logic judgment problem: (1) Is the relatively ineffective due to precision? (2) Is the priority in the expression incorrect? (3) Is the logic judgment reversed? Important loop issues: (1) Is the loop termination condition not correct? (2) Do you have normal termination (dead cycle)? (3) Is it incorrectly modified a loop variable? (4) Is there an error accumulation? Important error handling problem: (1) Do you forget to perform an error handling? (2) Error handling program block has not been operational? (3) Is there a problem with the error handling program block itself? If the error report is inconsistent with the actual error, the processing method is incorrect. (4) Is the error handling program block? The software has been mistaken before being called by it. Important Document I / O Question: (1) Do you operate on the non-existing or wrong file? (2) Is the file open in an incorrect manner? (3) Is the file end judgment incorrect? (4) Isn't it correctly closed? Appendix B: C / C test

This test is only used to examine the basic programming skills of C / C programmers. The content is limited to C / C common grammar, does not involve data structures, algorithms, and deep syntax. The test scores reflect the quality of the programming and the degree of understanding of C / C, but can not reflect the intelligence and software development capabilities of candidates. The written test time is 90 minutes. Please ask the candidates to carefully answer questions.

First, please fill in the IF statement of BOOL, FLOAT, pointer variables and "zero" comparison. (10 points)

Tip: Here "zero" can be 0, 0.0, false, or "empty pointer". For example, INT variable N and "zero value" comparison IF statement is:

IF (n == 0) IF (n! = 0)

Push it in this class.

Please write the IF statement of BOOL FLAG and "Zero" comparison: Please write the IF statement of Float X and "Zero" comparison: Please write the IF statement of char * p and "zero" comparison:

Second, the following is the 32-bit C program under Windows NT, calculate the value of SIZEOF (10 points)

Char str [] = "hello"; char * p = Str; int n = 10; calculates sizeof (STR) = sizeof (p) = sizeof (n) = void func (char STR [100]) {please calculate SIZEOF (STR) =} void * p = malloc (100); please calculate SizeOf (P) =

Third, a brief answer (25 points)

1. What is the use of IFNDEF / Define / Endif in the header file?

2, # include and #include "filename.h" What is the difference?

3, what is the use? (Please explain at least two)

4. Call the function after being compiled by the C program, why do you want to declare the extern "C"?

5. Please briefly describe the advantages and disadvantages of the following two for loops.

// First for (i = 0; i Advantages: Disadvantages: Advantages: Disadvantages:

4. Thoughts on memory (20 points)

Void getMemory (char *) {p = (char *) malloc (100);} void test (void) {char * str = null; getMemory (STR); strcpy (str, "hello world"); Printf (STR) ); What kind of results will there be a Test function? Answer: Char * getMemory (void) {char p [] = "Hello World"; Return P;} void test (void) {char * str = null; str = getMemory (); Printf (STR);} Work test What kind of results will the function? A: void getMemory2 (char ** p, int num) {* p = (char *) malloc (num);} void test (void) {char * str = null;

GetMemory (& STR, 100);

STRCPY (STR, "Hello");

Printf (STR);

} What kind of results will there be a Test function? Answer: Void test (void) {char * str = (char *) malloc (100); struct (str); free (str); if (str! = Null) {structure (str, "world" PRINTF (STR);}} What kind of results will there be a Test function? answer:

Five, write strcpy functions (10 points)

Know the prototype of the STRCPY function is

Char * STRDEST, Const Char * strsrc);

STRDEST is the destination string, and strsrc is a source string.

(1) Do not adjust the C / C string library function, please write the function strcpy

(2) STRCPY can copy the contents of strsrc to strDest, why should the return value of the type?

6. Write the constructor, destructor and assignment function (25 points)

The prototype of the known String is:

Class string {public: string (const char * str = null); // Ordinary constructor string (const string & other); // copy constructor ~ string (void); // Destructure function string & operate = (const string & Other); // Assign value private: char * m_data; // Used to save strings}; please write the above four functions of String.

Appendix C: Answer and Rating Standard for C / C Test

First, please fill in the IF statement of BOOL, FLOAT, pointer variables and "zero" comparison. (10 points)

Please write the IF statement of BOOL FLAG and "Zero" comparison. (3 points) Standard answer: if (flag) if (! Flag) is a bad style, not score.

IF (Flag == True)

IF (Flag == 1)

IF (Flag == False)

IF (Flag == 0) Please write the IF statement of Float X and "Zero" comparison. (4 points) Standard answer example: const float epsinon = 0.00001; if ((x> = - EPSINON) && (x <= epsinon) Do not use "==" or "! =" With digital comparison, should Try to translate into "> =" or "<=" such a form. The following is the wrong way of writing, no score .IF (x == 0.0)

IF (x! = 0.0)

Please write the IF statement of char * p and "zero value". (3 points) Standard answer: if (p == null) if (p! = Null) is a bad style, no score.

IF (p == 0)

IF (p! = 0)

IF (p)

IF (!)

Second, the following is the 32-bit C program under Windows NT, calculate the value of SIZEOF (10 points)

Char str [] = "Hello"; char * p = Str; int n = 10; please calculate SIZEOF (STR) = 6 (2 points) SIZEOF (P) = 4 (2 points) SIZEOF (N) = 4 (2分) Void func (char STR [100]) {please calculate SIZEOF (STR) = 4 (2 points)} void * p = malloc (100); please calculate SizeOf (P) = 4 (2 points)

Third, a brief answer (25 points)

1. What is the use of IFNDEF / Define / Endif in the header file? (5 points)

A: Prevent the header from being repeatedly referenced.

2, # include and #include "filename.h" What is the difference? (5 points)

A: For the #include , the compiler starts searching FileName.h from the standard library path to #include "filename.h", the compiler starts searching from the user's work path to find filename.h

3, what is the use? (Please explain at least two) (5 points)

Answer: (1) You can define a Const Constant (2) Const can modify the parameters of the function, return the value, and even the definition of the function. Things that are modified by const are mandatory, prevent accidents, can improve the robustness of the program.

4. Call the function compiled by the C program, why do you want to add externaln "c"? (5 points)

A: C language support function overload, the C language does not support function overload. The function is compiled by C and the name in the library is different from the C language. Assuming a function of a function is: Void foo (int X, int y); the function is compiled by the C compiler to _foo in the library, while the C compiler produces a name like _foo_int_int. C provides a C-connection exchange specifying symbol EXTERN "C" to solve the name match problem. 5. Please briefly describe the advantages and disadvantages of the following two for cycles (5 points)

For (i = 0; i

Fourth, the story of memory (5 points for each small question, a total of 20 points)

Void getMemory (char *) {p = (char *) malloc (100);} void test (void) {char * str = null; getMemory (STR); strcpy (str, "hello world"); Printf (STR) ); What kind of results will there be a Test function? A: The program crashes. Because getMemory does not pass dynamic memory, the STR in the TEST function has always been NULL. STRCPY (STR, "Hello World"); will make the program crash. Char * getMemory (void) {char p [] = "Hello World"; returnom p;} void test (void) {char * str = null; str = getMemory (); printf (str);}, please ask the Test function What kind of results? A: It may be garbled. Because getMemory returns a pointer to "stack memory", the address of the pointer is not null, but its original content has been cleared, and the new content is unknown. Void getMemory2 (char ** p, int num) {* p = (char *) Malloc (NUM);} void test (void) {

CHAR * STR = NULL;

GetMemory (& STR, 100);

STRCPY (STR, "Hello");

Printf (STR);

} What kind of results will there be a Test function? Answer: (1) Output Hello (2) memory leak void test (void) {char * str = (char *) malloc (100); strcpy (Str, "Hello"); Free (STR); if (STR! = NULL) {STRCPY (STR, "World"); Printf (STR);}} What kind of results will there be a Test function? A: Tamper with the content of the dynamic memory area, the consequences are difficult to expect, very dangerous. Because Free (STR); then, the STR becomes a wild pointer, if (str! = Null) statement does not work. Five, write strcpy functions (10 points)

Know the prototype of the STRCPY function is

Char * STRDEST, Const Char * strsrc);

STRDEST is the destination string, and strsrc is a source string.

(1) Do not adjust the C / C string library function, please write the function strcpy

Char * STRDEST, Const Char * strsrc);

{Assert ((strDest! = null) && (strDest! = null)); // 2 points char * address = strDest; // 2 points While (* strDest = * strsrc )! = '/ 0') ////// 2 points null; return address; // 2 points}

(2) STRCPY can copy the contents of strsrc to strDest, why should the return value of the type?

A: In order to achieve a chain expression. // 2 minutes

For example INT length = Strlen (STRDEST, "Hello World");

6. Write the constructor, destructor and assignment function (25 points)

The prototype of the known String is:

Class string {public: string (const char * str = null); // Ordinary constructor string (const string & other); // copy constructor ~ string (void); // Destructure function string & operate = (const string & Other); // Assign value private: char * m_data; // Used to save strings}; please write the above four functions of String. Standard answer: // String destructor

String :: ~ String (void) // 3 points

{

delete [] m_data;

// Since M_DATA is the internal data type, it is also possible to write a delete m_data;

}

// String ordinary constructor

String :: string (const char * STR) / / 6 points

{if (str == null) {m_data = new char [1]; // If you can add NULL to determine, better * m_data = '/ 0';} else {Int length = Strlen (STR); m_data = new char [Length 1]; // If you can add NULL to determine, better struct (m_data, str);}}

// Copy constructor string :: string (const string & other) // 3 points {Int length = Strlen (Other.m_data);

m_data = new char [length 1]; // If you can add NULL, it is better.

STRCPY (m_data, other.m_data);

}

// Assignment function string & string :: Operate = (const string & other) // 13 points {// (1) Check self-assignment // 4 points if (this == & other) return * this; // (2) Release Original memory resources // 3 points DELETE [] m_data; // (3) Assign new memory resources and copy content / 3 points

INT length = strlen (other.m_data);

m_data = new char [length 1]; // If you can add NULL, it is better.

STRCPY (m_data, other.m_data); // (4) Return to this object // 3 points return * this;}