Repost>

xiaoxiao2021-03-13  178

Note: This is a good article that C / C programmers should read. It is really a crystallization of master experience. Instead of only having a knowledge and wisdom, it means, even if you are a school computer system. Professor, very proficient in these two languages, if there is no project experience, it is unlikely to write such an inspirated article.

Thanks to the author of Lin Rui!

High Quality C / C Programming Guide

File status [] Draft file [√] official file [] Change formal file file ID: Current version: 1.0 Author: Dr. Lin Rui completed the date:

July 24, 2001

Version history

Version / Status Author Participant Start Date Remarks V 0.9 Draft Lin Rui

2001-7-1

to

2001-7-18

Lin Rui drafted V 1.0 official document Lin Rui

2001-7-18

to

2001-7-24

Zhu Honghai review V 0.9, Lin Rui corrects the mistake in the draft

table of Contents

Preface ... 6

Chapter 1 file structure ... 11

1.1 Copyright and version declaration ... 11

1.2 Structure of header files ... 12

1.3 Defining the structure of the file ... 13

1.4 role of header files ... 13

1.5 directory structure ... 14

Chapter 2 Processes of the Program ... 15

2.1 blank line ... 15

2.2 code line ... 16

2.3 Space in the code line ... 17

2.4 alignment ... 18

2.5 long line split ... 19

2.6 Position of the modifier ... 19

2.7 Notes ... 20

2.8 class layout ... 21

Chapter 3 Name Rules ... 22

3.1 Community Rules ... 22

3.2 Simple Windows Application Names Rules ... 23

3.3 Simple UNIX Application Names Rules ... 25

Chapter 4 Expression and Basic Sentences ... 26

4.1 Priority of the operator ... 26

4.2 composite expressions ... 27

4.3 IF statement ... 27

4.4 Efficiency of cyclic statements ... 29

4.5 Cyclic control variables for for statement ... 30

4.6 Switch statement ... 30

4.7 goto statement ... 31

Chapter 5 constants ... 33

5.1 Why do you need a constant ... 33

5.2 Const with #define ... 33

5.3 Constant definition rules ... 33

Constants in the 5.4 class ... 34

Chapter 6 Function Design ... 36

6.1 rules of parameters ... 36

6.2 Return value rules ... 37

6.3 Rules within the function of functions ... 39

6.4 Other recommendations ... 40

6.5 Using assertions ... 41

6.6 References and pointers comparison ... 42

Chapter 7 Memory Management ... 44

7.1 Memory allocation method ... 44

7.2 Common memory errors and their countermeasures ... 44

7.3 Comparison of pointers and arrays ... 45

7.4 How does the pointer parameter pass memory? ... 47

7.5 How is Free and Delete how is the pointer? ... 50

7.6 Does the dynamic memory are released automatically? ... 50

7.7 Eliminate "wild pointer" ... 51

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

7.9 What should I do if the memory consumption? ... 53

7.10 malloc / free use points ... 54

7.11 New / Delete's Use Tits ... 55

7.12 Some experiences ... 56

Chapter 8 Advanced Characteristics of C Functions ... 57

8.1 Function Overloaded Concept ... 57

8.2 Retention of Member Functions, Coverage and Hide ... 60

8.3 Default value for parameters ... 63

8.4 operator overload ... 64

8.5 Function Inline ... 65

8.6 Some experiences ... 68

Chapter 9 Constructor, Destructor and Assignment Function ... 69

9.1 Construction function and the origin of the destructor ... 69

9.2 Initialization Table of Constructor ... 70

9.3 Structure and sequence ... 72

9.4 Example: Constructor of class String and destructor ... 729.5 Do not underestimate the copy constructor and assignment function ... 73

9.6 Example: Copy constructor of class string and assignment function ... 73

9.7 Laying method to handle copy constructor and assignment function ... 75

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

9.9 Some experiences ... 77

Chapter 10 Inheritance and Combination ... 78

10.1 inheritance ... 78

10.2 combination ... 80

Chapter 11 Other Programming Experience ... 82

11.1 Using Const to increase the robustness of the function ... 82

11.2 Improve the efficiency of the program ... 84

11.3 Some beneficial recommendations ... 85

Reference ... 87

Appendix A: C / C Code Review Form ... 88

Appendix B: C / C test ... 93

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

Forehead

Software quality is hung by most programmers instead of putting things on your heart!

In addition to full outgoing lines and real programming masters, you will read this book, your first feeling will be panic: "Wow! How can I have so many problems with C / C procedures before?"

Don't be sad, the author is just a few more than you have been more panic.

Please spend more than two hours to read this paplial scriptures, you will be beneficial, this is the suggestion of the N-1 readers in front.

First, programming old hands and masters

Since the computer has been advent, the programming has become an enviable profession. The programmer is easy to develop into a group of problems that are often self-smelling after being favored.

The "real" programmer currently passed on the Internet is said to be like this:

(1) (1) The real programmer does not have a progress table, and only the leadership of the arrogant feet has progress table, and the real programmer will let the leadership will hang.

(2) (2) The real programmer does not write instruction manual, and the user should go to the function of the program.

(3) (3) The real programmer almost does not write the code, if the comment is difficult to write, it is hard to read.

(4) (4) The real programmer does not draw a flow chart, the primitive and illiteracy will do this.

(5) (5) The real programmer does not look at the reference manual, the novice and the ghost will see.

(6) (6) The real programmer does not write documents and does not need a document. Only if you can't understand the programs, you can use the document.

(7) (7) Real programmers think that they know more than users.

(8) (8) The real programmer does not accept the concept of team development unless he is his head.

(9) (9) The real programmer's procedure will not run correctly in the first time, but they are willing to keep the machine for several 30 hours of debugging.

(10) (10) The real programmer will not work between 9:00 am to 5:00 pm, if you see him at 9:00 am, this shows that he has been dry now last night.

......

The more the above features are, the more levels are high, and the qualifications are old. So don't be strange, many of the shortcomings of programmers can be appreciated as the advantages. Just like the martial arts novels, those who are alone, unconstrained, and a little evil master is the most worship. I used to believe in this, and I hope that I have become the "real" programmer, the result is not good.

I have been working hard from reading a college to doctors, and I have prepared hundreds of thousands of C / C code. Have such hardships and fatigue, should I be called the programming vetera?

The software I developed with scientific research (integrated circuit CAD and 3D graphics), tens of thousands of row procedures, complex technology, difficulty. These software frequently win, there is a software to get the first prize of the first China University Student Computer Competition. A set of graphics and software libraries developed in 1995, there are still people in 2000. Luo lists these "performance", you can explain that I am a programming master? Unfortunately, this personal feeling does not mean the fact.

During the reading blog, I have developed a 3D graphics software product of nearly 100,000 lines of C code for a year, and I ask for a true software masters in my heart. Although he never involved in the 3D graphic field, he pointed out multiple major design errors in dozens of software. It feels that the software is a gorgeous dress with paper paste, pulling a piece, poke a hole. I was stunned to realize that this software has no practical value, a year's heart and blood, and killed his software company.

People's merits usually happen to the most painful moment, after depression and heartache, I made a deep reflection of "facial wall" for half a year, re-examining the basics of software design. After the supplement "internal strength", I feel that the waist is hard. In the first half of the year after graduation, I used to find a job to the Microsoft China Institute and accept a senior software engineer of Microsoft. He lets me write a function of Strcpy.

Too easy?

wrong!

Such a small function, he examines these three aspects:

(1) Programming style;

(2) Error handling;

(3) Algorithm complexity analysis (for improved performance).

No one has been so strictly examined in the university. I have been in half an hour, revised several times, he is not satisfied, let me go home. I am in the spirit to enter the "examination room" and sweat the "examination". This "master" is too much. I will reflect again.

I wrote the reficed experience to write an article on the Internet to spread, causing a lot of software developers to resonate. I have so I am fortunate to have a wide exchange of comrades from China, large IT companies such as Huawei, Shanghai Bell, ZTE and other companies. Everyone believes that improving quality and productivity is the core problem to be solved by software projects. High quality programming is a very important link, after all, software is implemented by programming.

Can our vetets and masters to write high quality programs?

Can not be met!

As far as my experience and experience, the computer education of domestic universities did not instill the concept of high-quality programming, and teachers and students also consciously care about the quality of software. The procedure of diligence is long, and there is a lot of experience in the low quality program. After eating suffering, there are some experiences, and I am very slow, I am an example.

The software developers of bachelor, master, and doctoral diploma are now everything, but they will suddenly realize the quality of "innate" when they accept college education. How many software developers have quality attributes such as correctness, robust, reliability, efficiency, ease of use, readability (understandability), scalability, reuse, compatibility, portability, etc. ? And can it be used in practice? . "High quality" can be achieved without working care!

We have a full reason to doubt:

(1) Programming veterans may be programmed for a long time to use hidden mistakes (habits into nature), I don't want to believe that it is true after it is true!

(2) Programming master can write a very horizontal code in a certain field, but it is not necessary to grasp the square quality of the software quality.

It turns out that this is true. I went to Shanghai Bell for a year, one after another, interviewed or tested nearly 100 "new" "old" programmers programming skills, and the quality qualification rate is approximately 10%. Few people can write an IF statement that fully meets the quality requirements, many programmers have a semi-solving, memory management, and ...... The leaders can't believe this is true. I have done a live trial: There is a sector new 14 master student. I will conduct "C / C programming skills" to test the bottom test before opening the meeting. I ask everyone how difficult is it difficult? All people have answered difficult. The result is no one person, and half of people have zero. The friends of the competitor have also been experimenting, and they are equally alive.

It's really not that I "my heart," or high requirements, but many software developers are not high enough for their own requirements.

You must know that the employee quality of Huawei, Shanghai Bell, ZTE is more preceding in domestic IT companies. If their programming quality is so bad, what do we dare to expect SMEs to take high quality software? Even the program is not compiled, but also talk about the revitalization of the national software industry, don't worry.

I am going to define the programming veterans and programming masters, please don't know.

Definition 1: Programmer with a long-term stable programmer is called programming old hands.

Definition 2: Programmer with high-quality programs that can have long-term stable and high quality programs called programming masters.

According to the above definition, you will get the first inference immediately: I am neither a master is not an older.

Before writing this book, I read a lot of English writing works, the more shy. Because you find that you have the basic skills of our programming failure, you will have a second-class level, but also think about any old hands and masters. I hope to be the same as the domestic native of the country, and I can do it:

(1) If you know the error, change;

(2) It is often known for normal temperature;

(3) Adhere to learning, every day.

Second, 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.

If your programming quality is already too close, don't satisfy it. If you want to become an excellent software developer, I suggest you read and follow the CMMI specification to make your own level rise to a step. Shanghai Bell's employees can request CMMI information to the software engineering research team of the Network Appliances, it is best to participate in the training.

Third, copyright statement

Most of this book is made of books in the author's book (not yet published). It is now constructed as a standardized document for the Shanghai Bell Network Applied Division, as a training materials.

Since C / C programming is well known technique, there is no secret. Good experience in programming should be shared, and we are doing this. The author is willing to disclose the electronic document of this book.

Copyright statements are as follows:

(1) The reader can copy, modify the content of the book, but not to tamper with the author and the unit.

(2) This book must not be published or issued without the license.

(3) If the staff of the competitor gets this book, please do not use it to avoid disputes.

It is estimated that by July 2002, we will establish a CMMI 3 solution for China's national conditions. For about 1000 pages, including this book, about 1000 specification will be strictly controlled.

Welcome readers to propose criticisms to this book.

Lin Rui, July 2001

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 Bell Co., Ltd. Network Application Division

* All Rights Reserved.

*

* File Name: filename.h

* File ID: See Configuration Management Program

* Summary: Summary describes the contents of this document

*

* Current version: 1.1

* Author: Enter the author (or modifier) ​​name

* Completion Date:

July 20, 2001

*

* Replacement version: 1.0 * Original Author: Enter the original author (or modifier) ​​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.

ll [rule

1-2-1

】 To prevent the header from being repeatedly referenced, the preparation block should be generated using the IFNDEF / DEFINE / ENDIF structure.

ll [rule

1-2-2

】 Use a #include format to reference the standard library's header file (the compiler will start searching from the standard library directory).

ll [rule

1-2-3

】 Use a #include "filename.h" format to reference the unbalanced header file (the compiler will start searching from the user's working directory).

2 2 [suggestion

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.

2 2 [suggestion

1-2-2

】 Do not advocate global variables, try not to appear in the header file in the header file. 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 of the non-standard library

...

Void function1 (...); // global function declaration

...

Class box // class structure declaration

{

...

}

#ENDIF

Example 1

-2 C

/ C header structure

1.3 Defining the structure of the file

The definition file has three parts:

(1) (1) Define the copyright and version declaration at the beginning of the file (see Example 1-1).

(2) (2) Quote for some headers.

(3) (3) 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

...

// implementation of global functions

Void function1 (...)

{

...

}

Property of // class member function

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.

ll [rule

2-1-1

】 After each class declaration, the row must be added after each function definition. See example 2-1 (a)

ll [rule

2-1-2

】 In a function body, it is not vacuum between the lack of closely related statements, and the line should be separated elsewhere. See example 2-1 (b)

//

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

ll [rule

2-2-1

】 One-line code only does one thing, if only one variable is defined, or write only one statement. This code is easy to read and is convenient to write a comment.

ll [rule

2-2-2

】 IF, for, while, do, other statements, 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; // height

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

{

DOSMETHING ();

}

WiDTH

For (Initialization; Update)

{

DOSMETHING ();

}

//

Other ();

For (Initialization; Update)

DOSMETHING ();

Other ();

Example 2-2 (a) Good style code line example 2-2 (b) Code row in style

2 2 [suggestion

2-2-1

】 Initialize this 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 h

INT depth = 10; // Define and initiate DEPTH

2.3 Space in the code line

ll [rule

2-3-1

】 Several spaces after the keyword. At least one space is left after const, virtual, inline, case, etc., otherwise the keyword cannot be analyzed. You should leave a space and then with the left bracket '(' to highlight keywords) after the keywords such as if, for, while.

ll [rule

2-3-2

】 Do not leave the space after the function name, keep up with the left bracket '(' to distinguish between keywords.

ll [rule

2-3-3

】 '(' Tight Tie, ')', ',', ';' The go is tight, keeping the space at the space.

ll [rule

2-3-4

】 ', Then leave space, such as Function (X, Y, Z). If ';' is not the end symbol of a row, then leave spaces, such as for (Initialization; Condition; Update).

ll [rule

2-3-5

】 Assigning operators, compare operators, arithmetic operators, logical operators, bit domain operators, such as "=", " ="> = "," <= "," "," * "," %, "&&", "||", "<<", "^", etc., the binary operator should be added.

ll [rule

2-3-6

】 One-dollar operator is like "!", "~", " ", "-", "&", etc., there is no space before and after.

ll [rule

2-3-7

】 "[]", ".", "->" does not add space before and after.

2 2 [suggestion

2-3-1

】 For a relatively long for a longer, if the IF statement, some spaces can be removed for compact, 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) // bad 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 ) / defvearted style

For (i = 0; i <10; i ) // too many spaces

X = a

X = a

INT * x = & y; // Good style

INT * x = & y; // bad style

Array [5] = 0; // Don't write Array [5] = 0;

A.Function (); // Don't write a. Function ();

B-> function (); // Don't write into b -> function ();

Example 2-3 Space in the code line

2.4 alignment

ll [rule

2-4-1

】 The delimiter '{' and '}' of the program should be exclusively and in the same column, and is aligned with the statement that references them.

ll [rule

2-4-2

The code block within {} is aligned in 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

}

IF (condition)

{

... // Program Code

}

Else

{

... // Program Code

}

IF (Condition) {

... // Program Code

}

Else {

... // Program Code

}

For (Initialization; Update)

{

... // Program Code

}

For (inTIALIZATION; 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

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

ll [rule

2-5-2

The long expression is to be split into a new line at a low priority operator, and the operator is placed in the first of the new row (in order to highlight the operator). 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)))

{

DOSMETHING ();

}

Virtual Cmatrix CMULTIPLYMAMATRIX (CMAMATRIX Leftmatrix,

CMAMATRIX Rightmatrix;

Very_Longer_Initialization;

Very_longer_condition;

VERY_LONGER_UPDATE)

{

DOSMETHING ();

}

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.

ll [rule

2-6-1

】 The modifier * and & & climb

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.

ll [rule

2-7-1

The comment is "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.

ll [rule

2-7-2

If the code is clear, you don't have to add any comments. Otherwise, you will be bored. E.g

i ; // i plus 1, extra comment

ll [rule

2-7-3

】 Side Write Code Edges, modify the code simultaneously modify the corresponding comments to ensure the consistency of comments and code. Annotation that is no longer deleted.

ll [rule

2-7-4

】 The annotation should be accurate, easy to understand, prevent the annuality of notes. The wrong note is not only harmful. ll [rule

2-7-5

】 Try to avoid using abbreviations in the comments, especially uncommonly.

ll [rule

2-7-6

The position of the annotation should be adjacent to the code described, and can be placed above or right, and cannot be placed below.

ll [rule

2-7-8

When the code is long, especially 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 a lot of people's experience - "Don't just let yourself think clearly when designing classes, and convenient for others to read. Because users are most concerned about the interface, who is willing to see a bunch of private data members!"

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" law is cumbersome, for example

INT I, J, K;

Float X, Y, Z;

If the "Hungary" naming rules are used, they should be written.

INT II, ​​IJ, IK; // Prefix i Represents INT Type

FLOAT FX, FY, FZ; // Prefix F Represents Float Type

Such a cumbersome procedure will make the most programmers can't stand it.

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.

ll [rule

3-1-1

】 The identifier should be intuitive and can be spent, and it is desirable to know that "decoding" is not required.

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.

ll [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 the meaning, so the function name, variable name, and class names are not blame for more than a dozen characters. 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.

ll [rule

3-1-3

】 Naming rules as much as possible 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.

ll [rule

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);

ll [rule

3-1-5

】 Do not appear on the identifier and global variables in the program, although both the role domain of the two do not have a grammatical error, it will misunderstand.

ll [rule

3-1-6

】 The name of the variable should use "noun" or "adjective noun".

E.g:

FLOAT VALUE;

Float oldValue;

Float newValue;

ll [rule

3-1-7

】 The name of the global function should use "verb" or "verb noun" (mobile phrase). 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

ll [rule

3-1-8

】 Named the correct antisense word group named mutually exclusive variables or function of the opposite action.

E.g:

Int minValue;

Int maxValue;

Int setValue (...);

Int getValue (...);

2 2 [suggestion

3-1-1

】 Try to avoid digital numbers in the name, such as Value1, Value2, etc., unless logically required numbers. 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.

ll [rule

3-2-1

】 The class name and function name are combined with a word at which the uppercase letters begin.

E.g:

Class node; // class name

Class LeafNode; // Classification

Void Draw (void); // Function Name

Void setValue (int value); // function name

ll [rule

3-2-2

】 Variables and parameters are combined with words with lowercase letters.

E.g:

Bool flag;

Int drawMode;

ll [rule

3-2-3

】 Constants use uppercase letters and segment words with underlined lines.

E.g:

Const int max = 100;

Const int max_length = 100;

ll [rule

3-2-4

】 Static variable prefix S_ (represents static).

E.g:

void init (...)

{

Static int s_initvalue; // static variable

...

}

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

ll [rule

3-2-6

】 The data member of the class adds M_ (represents member) so that the data member is the same name with the member function.

E.g:

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

{

m_width = width;

m_height = height;

}

ll [rule

3-2-7

】 To prevent some identifiers in a single software library and conflicts in other software libraries, you can add a prefix that can reflect software properties for 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

Binding law

From

high

Until

low

row

Column

() [] ->.

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 left to right

||

from right to left

?:

from right to left

= = - = * = / =% = & = ^ =

| = << = >> =

from left to right

Table 4-1 Priority and Combination Law of Operators

ll [rule

4-1-1

】 If the operators in the code line are more, with parentheses to determine the order of operations, 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.

ll [rule

4-2-1

】 Do not write too complex composite expressions.

E.g:

i = a> = B && c

ll [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;

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

ll [rule

4-3-1

】 The Boolean variable cannot be compared directly 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 the standard IF statement compared to zero value as follows: if (flag) // means 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 variable and zero value

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

ll [rule

4-3-3

】 The floating point variable cannot be compared to any number with "==" or "! =".

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

Comparison of pointer variables and zero values

ll [rule

4-3-4

】 The pointer variable should be compared to 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)

Don't write

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 instructions for the IF statement

Sometimes 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 of the following bad style should be

IF (condition)

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

4-4-1

】 In multiple cycles, if possible, the longest cycle should be placed in the innermost layer, and the shortest circulation is placed in the outermost layer to reduce the number of CPUs cross-cutting cycles. 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

ll [suggestion

4-4-2

】 If there is a logical judgment in the circulation, the number of cycles is large, 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

{

IF (condition)

DOSMETHING ();

Else

Doothershing ();

}

IF (condition)

{

For (i = 0; i

DOSMETHING ();

}

Else

{

For (i = 0; i

Doothershing ();

}

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

ll [rule

4-5-1

】 The loop variable cannot be modified within the FOR cycle, preventing the FOR cycle from being lost control.

ll [suggestion

4-5-1

】 The value of the loop control variable of the recommendation for statement is written by "semi-open semi-closed interval".

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

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

-1"

At the start point to the interval of the end point is N-1, the number of cycles is N.

In contrast, examples 4-5 (a) are more intuitive, although the functions of both are the same.

For (int x = 0; x

{

...

}

For (int x = 0; x <= n-1; x )

{

...

}

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;

}

ll [rule

4-6-1

】 End of each CASE statement should not forget to add BREAK, otherwise multiple branch overlaps (unless intentionally overlapping multiple branches).

ll [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 constants

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 you need a constant?

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

(1) (1) Readability of the program (understandable) is deteriorated. The programmer will forget what the figures or strings mean, and the user doesn't know where they come, what is it.

(2) (2) Enter the same number or string in many places in the program, and it is difficult to prevent writing errors.

(3) (3) If you want to modify the number or string, it will be changed in many places, which is both trouble and easy.

ll [rule

5-1-1

】 Try to use a meanled intuitive constant to represent numbers or strings that will appear multiple times in the program.

E.g:

#define max 100 / * C language Macroembox * /

Const int max = 100; // C language Const 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:

(1) (1) constant has a data type, while the macro regular amount 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.

(2) (2) Some integrated debugging tools can debug the Const constant, but cannot debug the macro.

ll [rule

5-2-1

】 Only const constants are used in C programs without using a macro pattern, that is, the constant constant is completely replaced.

5.3 Constant definition rules

ll [rule

5-3-1

】 It is necessary to place a constant on the outer disclosure in the header file, and there is no need to place the heads of the definition file on the existing constant. To facilitate management, you can store the constant concentration of different modules in a common header file.

ll [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, attempted to initialize Const data members in class declarations

INT Array [size]; // error, unknown SIZE

}

The initialization of Const data members can only be performed in the initialization table of the class constructor, for example

Class A

{...

A (int size); // constructor

CONST INT Size

}

A :: A (int size): initialization table of size (size) // constructor

{

...

}

A a a (100); // // The size value of the object A is 100

A b (200); // The Size value of the object B is 200

How can I build constant constant in the entire class? Don't expect const data members, you should implement the enumeration constants in the class. E.g

Class A

{...

ENUM {size1 = 100, size2 = 200}; // enumeration

Int Array1 [Size1];

Int array2 [size2];

}

Enumerations often do not take up the storage space of the object, and they are fully evaluated when compiling. 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

ll [rule

6-1-1

】 The writing of the parameters should be complete, do not save the type of only the type of parameter 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

ll [rule

6-1-2

】 The parameter naming is appropriate, 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

ll [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);

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

2 2 [suggestion

6-1-1

】 Avoid functioning with too much parameters, and 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. 2 2 [suggestion

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

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

ll [rule

6-2-2

】 Function Name and Return Value Types 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.

ll [rule

6-2-3

】 Do not return normal values ​​and error signs to return. 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?

2 2 [suggestion

6-2-1

】 Sometimes the function does not require return values, 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");

2 2 [suggestion

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 '

String

Temp.data = new char [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.

ll [rule

6-3-1

】 In the "entrance" of the function, check the validity of the parameters.

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

6-3-2

】 In the "exit" of the function, 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 errors

}

(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

2 2 [suggestion

6-4-1

】 The function of the function is to be single, do not design multi-purpose functions.

2 2 [suggestion

6-4-2

】 The size of the function body is small and should be controlled within 50 lines of code.

2 2 [suggestion

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.

2 2 [suggestion

6-4-4

】 Not only should you check the validity of the input parameters, but also check the validity of variables in the function in the function in other ways, such as global variables, file handles, etc.

2 2 [suggestion

6-4-5

】 The return value for error processing must be clear, so that the user does not easily ignore or misunderstand the error.

6.5 use assertions

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 * pvto, const void * pvfrom, size_t size)

{

Assert ((PVTO! = null) && (pvfrom! = null); // Using assertions

Byte * PBTO = (byte *) PVTO; / / Prevent the address of the PVTO

Byte * pbfrom = (byte *) PVFROM; / / Prevent the address of the PVFROM

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]

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

ll [rule

6-5-2

】 At the entrance to the function, the validity of the assertion check parameters (legitimacy) is used.

ll [suggestion

6-5-1

】 When writing a function, it is necessary to perform repeated exam, and ask: "What assumptions I plan?" Once the assumption is determined, it is necessary to check the assumptions using the assertion.

ll [suggestion

6-5-2

】 General textbooks encourage programmers to perform anti-wrong design, but remember 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 rules referenced are as follows:

(1) The reference must be initialized while being created (the 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

Compare the above three sample programs, the nature of "reference delivery" is found, "pointer delivery", and writing method is like "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

- 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:

(1) (1) Assign from a 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.

(2) (2) Created 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.

(3) (3) Distribution 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:

U u Memory Allocation is unsuccessful, but used 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).

U u Memory Allocation is successful, but it has not been 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.

U u 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. u u I forgot to release memory, resulting in memory disclosure.

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

U u releases memory but continue 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".

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

ll [rule

7-2-2

】 Don't forget to pay initial values ​​in the array and dynamic. Prevent memory that will not be initialized as the right value.

ll [rule

7-2-3

】 Avoid the subsidiaries of arrays or pointers, especially be careful

1

"

Or "less

1

"

operating.

ll [rule

7-2-4

】 The application and release of dynamic memory must be paired to prevent memory leakage.

ll [rule

7-2-5

After using Free or Delete released the 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

Modified content

Example

7-3-1

In the middle, the capacity of the character array A is 6 characters whose 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 the constant string

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

Cout << p << endl; example

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

7-3-2

In, 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 use 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

Calculate memory capacity

Use operator SIZEOF to calculate the capacity of the array (number of bytes). Example

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

7-3-3

In (b), regardless of the capacity of array A, SIZEOF (A) is always equal to SIZEOF (Char *).

Char a [] = "Hello World";

Char * p = a;

COUT << SizeOf (a) << endl; // 12 byte

Cout << sizeof (p) << endl; // 4 byte

Example

7-3-3

(A) Calculate the memory capacity of arrays and pointers

Void Func (Char A [100])

{

COUT << SizeOf (a) << endl; // 4 byte instead of 100 bytes

}

Example

7-3-3

(B) array 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. Example

7-4-1

In the statement of the TEST function, GetMemory (STR, 200) did not make Str to get the expected 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

STRCPY (STR, "Hello"); // Run error

}

Example

7-4-1

Trying 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 do not apply for memory with a pointer parameter, you should use the "pointer to the pointer", see example

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 Parameters is & Str, not STR

STRCPY (STR, "Hello");

COUT << str << endl;

Free (STR);

}

Example

7-4-2

Apply a dynamic memory with a pointer to the pointer

Due to the concept of "pointers" pointing pointers ", we can use function return values ​​to pass dynamic memory. This method is simpler, see example

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

Use a function return value to pass the 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 you emphasize not to return to the "stack memory" pointer to the "stack memory", because the internal existence of the function is automatically done, see example

7-4-4

.

Char * getString (Void)

{

Char p [] = "Hello World";

Return P; // 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 memory"

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 put an example

7-4-4

Rewriting a sample 7-4-5, what will it?

Char * getString2 (void) {

Char * p = "Hello World";

Return P;

}

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 refers to release, but the address referred to in P is still constant.

...

IF (p! = null) // does not play the wrong force

{

STRCPY (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. For example, 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 usemallocfree (void)

{

Obj * a = (obj *) malloc (sizeof (obj)); // Apply for dynamic memory

A-> Initialize (); // Initialization

// ...

A-> Destroy (); // Clear work

Free (a); // Release memory

} Void UseNewelete (VOID)

{

Obj * a = new obj; // Apply for dynamic memory and initialize

// ...

Delete a; // Clear and release memory

}

Example 7-8 How to implement the dynamic memory management class OBJ of the object in Malloc / Free and New / Delete OBJ's function initialize simulates the function of 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 with Malloc to the integer type of the length of length Length, the program is as follows:

INT * P = (int *) malloc (sizeof (int) * length);

We should focus on two elements: "Type Conversion" and "SIZEOF".

The type of U u malloc returned value is VOID *, so it is necessary to explicitly turn the type conversion when calling Malloc, converts VOID * to the required pointer type.

The U u 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's "()" using the Sizeof operator is a good style, but should be careful, we will faint heads, write P = Malloc (SIZEOF (P)).

The U u Function Free's prototype 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); // No parameter constructor

Obj (int X); // Take a constructor with 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. E.g

Obj * Objects = new obj [100]; // Create 100 dynamic objects

Cannot write

Obj * Objects = new obj [100] (1); // Creating 100 dynamic objects while assigning initial value 1

Note not to lose symbol '[]' when you release an object array with Delete. E.g

Delete [] Objects; // correct usage

Delete Objects; // Error Usage

The latter is equivalent to Delete Objects [0], leaking off another 99 objects.

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 Concept of function overload

8.1.1

Overloaded origin

In the natural language, a word can have many different meanings, ie the word is overloaded. It can be judged from the context to which meaning is. "The overload of the word" can make the language more concise. For example, "Eating" has a wide range of people, and people don't have to say clearly what to eat every time. Don't be a hole in the hole, saying four kinds of writing. In the C program, several functions similar to the semantic and functionality can be represented by the same name, that is, the function is overloaded. This makes it easy to remember, improve the ease of use of functions, which is a reason for the use of overloading mechanisms in C languages. Example,

8-1-1

The function EATBEEF, EATFISH, EATCHCHCHICKEN can be represented by the same function name EAT, which is different from different types of parameters.

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

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

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

Example

8-1-1

Overload function EAT

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

8.1.2

How is overloaded?

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

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

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

Void function (void);

INT function (void);

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

INT x = function ();

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

Therefore, you can only distinguish the overload function with different parameters and cannot rely on the return value type. The compiler produces different internal identifiers to each overload function according to the parameters. For example, a compiler is an example

8-1-1

The three EAT functions generate like _eat_beef, _eat_fish, the internal identifier such as _eat_chicken (different compilers may generate 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

}

Written

EXTERN "C"

{

#include "myHeader.h"

... // Other C header files

}

This tells the C compiler, the function foo is a C connection, you should go to the library to find name _foo instead of find _foo_int_int. The C compiler developer has made Extern "C" on the header file of the C standard library, so we can directly reference these header files directly with #include. Note that it is not the same as the name of the two functions. The global function and the member function of the class are not overloaded because the function of the function is different. E.g:

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

Class A

{...

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

}

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

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

8.1.3

Current implicit type conversion causes the overload function to generate erliness

Example

8-1-3

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

# include

Void Output (int x); // function declaration

void output (float x); // function declaration

Void Output (INT X)

{

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

}

Void Output (Float X)

{

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

}

Void main (void)

{

INT x = 1;

Float y = 1.0;

Output (x); // Output INT 1

Output (y); // output float 1

Output (1); // Output INT 1

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

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

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

}

Example

8-1-3

Implicit type conversion causes the overload function to generate erliness

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

Overload and coverage

Member function is overloaded:

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

(2) The function name is the same;

(3) Different parameters;

(4) Virtual keywords can be available.

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

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

(2) The function name is the same;

(3) The same parameter; (4) The base class function must have a Virtual keyword.

Example

8-2-1

In, the function base :: f (int) is overloaded with Base :: F (Float), while Base :: g (void) is overwritten by Derived :: G (Void).

#include

Class Base

{

PUBLIC:

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

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

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

}

Class Derived: Public Base

{

PUBLIC:

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

}

Void main (void)

{

Derived D;

Base * Pb = & D;

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

Pb-> f (

3.14F

); // Base :: f (float) 3.14

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

}

Example

8-2-1

Overload and coverage of member functions

8.2.2

Confusing hidden rules

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

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

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

Sample program

8-2-2

(A) in:

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

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

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

#include

Class Base

{

PUBLIC:

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

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

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

}

Class Derived: Public Base

{

PUBLIC: Virtual Void F (Float X) {cout << "Derived :: F (FLOAT) << x << endl;}

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

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

}

Example

8-2-2

(A) overload, coverage and hiding of member functions

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.

Example

8-2-2

In (b), BP and DP points to the same address. It should be reasonable to follow the results of the operation.

Void main (void)

{

Derived D;

Base * Pb = & D;

Derived * pd = & D;

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

Pb-> f (

3.14F

); // derived :: f (float) 3.14

PD-> f

3.14F

); // derived :: f (float) 3.14

// Bad: Behavior Depends on Type of The Pointer

PB-> g

3.14F

); // Base :: g (float) 3.14

PD-> g

3.14F

); // derived :: g (int) 3 (surprise!)

// Bad: Behavior Depends on Type of The Pointer

PB-> h

3.14F

); // base :: h (float) 3.14 (surprise!)

PD-> h

3.14F

); // derived :: h (float) 3.14

}

Example

8-2-2

(B) comparison of overload, coverage and hidden

8.2.3

Get rid of hidden

The hidden rules have caused a lot of trouble. Example

8-2-3

In the program, the statement PD-> F (10) is the original meaning to call the function base :: f (int), but base :: f (int) is hidden by Derived :: F (Char *). Since the number 10 cannot be implicitly converted to a string, it is wrong when compiling.

Class Base

{

PUBLIC:

Void f (int X);

}

Class Derived: Public Base

{

PUBLIC:

Void F (Char * STR);

}

Void test (void)

{

Derived * pd = new deerid;

PD-> f (10); // error

}

Example

8-2-3

Because of hidden

From an example

8-2-3

It seems that hidden rules seem to be very stupid. But there are at least two reasons for hidden rules:

U u u u u u u u U User PD-> F (10) may really want to call the derived :: f (char *) function, just that he misuses the parameters wrong. With hidden rules, the compiler can clearly point out errors, which is not necessarily good. Otherwise, the compiler will quietly pass the wrong, and the programmer will hardly find this error, and the root will be found.

U u If the class derived has multiple base classes (multiple inheritance), sometimes it is unclear 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

In, 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:

ll [rule

8-3-1

】 The default value of the parameter can only appear in the declaration of the function, and cannot appear in the defined body.

E.g:

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

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

{

...

}

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

ll [rule

8-3-2

If the function has multiple parameters, the parameters can only be deficed from the back forward, otherwise the function call statement blame will cause the function.

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

8-3-2

The default value of the unreasonable use of the parameters will result in an emphasis on 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

}

Example

8-3-2

The default value of the parameters 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 an operator overload to indicate:

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

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

Complex A, B, C;

...

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

C = a b; // with operator

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

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

From the grammatical speaking, the operator can be defined as a global function or as a member function. Document [Murray, P44-P47] has made more elaborations for this issue, and summarizes the table

8-4-1

the rule of.

Operator

rule

All one yuan operator

Recommended to overreload as a member function

= () [] ->

Can only be overloaded as a member function

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

Recommended to overreload as a member function

All other operators

Recommended overload is a global function

table

8-4-1

Operator's overloading rules

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

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

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

8.4.2

Operators that cannot be overloaded

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

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

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

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

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

8.5 function inline

8.5.1

Use the inline to reject a macro code

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)

Statement

Result = max (i, j) 2;

Explain the pre-processor

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

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

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

If the macro code is rewritten as

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

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

Result = max (i , j);

Explain the pre-processor

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

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

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

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

8.5.2

Inline function programming style

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

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

Void foo (int x, int y)

{

...

}

And the following style function foo is an inline function:

Void foo (int x, int y);

Inline void foo (int x, int y) // inline and function definitions are set together {

...

}

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

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

Class A

{

PUBLIC:

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

}

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

// head File

Class A

{

PUBLIC:

Void foo (int x, int y);

}

// Define the file

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

{

...

}

8.5.3

Inline

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

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

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

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

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

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

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

8.6 Some experience

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

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 non-parameter constructor

A (const A & a); // default copy constructor ~ a (void); // default destructor

A & Operate = (const A & a); // Default assignment function

This is not forbidden, since it can automatically generate functions, why do you want 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); // Destructor

String & Operate = (const string & other); // assignment function

Private:

Char * m_data; // Used to save strings

}

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. (With the above, the literature is referred to [EEKEL, P55-P56])

9.2 Initialization Table of Constructor

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:

u u 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 U u class can only be initialized in the initialization table because it cannot be initialized in the functional body (see Section 5.4).

The initialization of the data member of the U u class can use the initialization table or function to assign two ways, 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. E.g

Class A

{...

A (void); // no parameter constructor

A (const a & other); // copy constructor

A & Operate = (const a & other); // assignment function

}

Class B

{

PUBLIC:

B (Const A & a); // B constructor

Private:

A m_a; // member object

}

In Examples 9-2 (a), the constructor of class B calls the copy constructor of class A in its initialization table, thereby initializing 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;

}

The constructor of f in Example 9-2 (c) The first initialization method is used, and the constructor of F in Example 9-2 (d) is a 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

{

INT length = 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:

u U U. This chapter says that if you do not actively write a copy constructor and assignment function, the compiler will automatically generate the default function in a "bit copy" mode. 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.

U u copy constructor and assignment function is very easy to confuse, often leading to something 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 & other)

{

// (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 to this object

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

B = & a;

...

a = * b;

Maybe someone will say: "Even if you have a self-assignment, I can ignore it, I don't make some time to let the object copy yourself, anyway, I won't be wrong!"

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 of your own value

IF (this == & other)

Wrong writing

IF (* this == taher)

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

E.g:

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 procedures:

A b (a); // Call the private copy constructor

B = a; // Call the private assignment function

The 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 U u derived class should call the constructor of the base class in its initialization table.

The destructor of the U u. 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

U u 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 & ta); // class base assignment function

Private:

INT M_I, M_J, M_K;

}

Class Derived: Public Base

{

PUBLIC:

...

Derived & Operate = (const derived & other); // class Derived assignment function

Private:

INT M_X, M_Y, M_Z;

}

Derived & Derived :: Operate = (const derived&)

{

// (1) Check the self-assignment

IF (this == & other)

RETURN * THIS;

/ / (2) Re-assapose data members of the base class

Base :: Operate = (other); // Because you can't directly operate private data members

// (3) Assignment of data members of the school

m_x = other.m_x;

m_y = other.m_y;

m_z = other.m_z;

/ / (4) Return to the reference 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.

ll [rule

10-1-1

】 If class A and class B are not associated, it is not possible to make B inherit A function and properties in order to make B functions. Don't think "White White Don't eat", let a good-end healthy youth to eat people to eat people.

ll [rule

10-1-2

If the "a Kind of) is logically b is A, the 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

{

...

}

u u User

【rule

10-1-2

】 It looks very simple, but there may be accidents 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

ll [rule

10-2-1

If the logical A is a "part" (a part of), B is not allowed to be born 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. Example

10-2-1

Indicated.

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

10-2-2

It is very short and the operation is correct, 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 is 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. "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 of a const modified function

If the parameter is used, no matter what the data type is, no matter 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:

u u If the input parameter is adopted "Pointer Transfer", then CONST modification prevents accidentally change the pointer to protect the protection.

For example, StringCopy functions:

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

STRSource is an input parameter, and 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.

u u If the input parameter is "value passed", the input parameters will not be protected since the function will automatically generate temporary variables, which does not need to be protected, so do not add Const modification.

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.

U u For parameters of the non-internal data type, the function of the function is determined 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 usage of "const &" modified input parameters, such as table

11-1-1

Indicated.

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

U u If a function returns a value to a "pointer" mode, the content returns (ie, the content) content cannot be modified, the return value can only be assigned to the same type of CONST modified. For example functions

Const char * getString (void);

The following statement will appear compilation:

CHAR * STR = getString ();

The correct usage is

Const char * str = getString ();

u u u u If the function return value uses "value delivery 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 U u function return value is not much in the case of "reference delivery", which typically only appears in the assignment function of the class, and the purpose is to achieve chain expression.

E.g

Class A

{...

A & Operate = (const a & other); // assignment function

}

Objects A A, B, C; // A, B, C is A

...

A = b = c; // Normal chain assignment

(a = b) = c; // abnormal chain assignment, but legal

If the return value of the assignment function is added to const modification, 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 the data 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 ELEM

INT POP (Void);

INT getCount (void) const; // constist member function

Private:

INT m_num;

INT M_DATA [100];

}

INT Stack :: getCount (void) Const

{ m_num; // Compiling errors, attempt to modify data members m_num

POP (); // Compiling errors, trying to call non-Const functions

Return m_num;

}

The statement of the Const member function looks whims: Const keyword can only be placed in the end 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.

l L [Rule 11-2-1

】 Don't pursue the efficiency of the program, you should try to improve the efficiency of the program under the premise of satisfying the quality factors such as correctness, reliability, robustness, readability.

ll [rule

11-2-2

】 The global efficiency of improving the program is mainly, and the local efficiency is supplemented.

ll [rule

11-2-3

】 When optimizing the efficiency of the program, you should first find the "bottleneck" of the restriction efficiency, do not optimize the insignificant.

ll [rule

11-2-4

】 First optimize the data structure and algorithm, and then optimize the execution code.

ll [rule

11-2-5

】 Sometimes time efficiency and spatial efficiency may be opposite, at this time, it should analyze the more important and appropriate compromise. For example, spend some memory to improve performance.

ll [rule

11-2-6

】 Do not pursue compact code because compact code does not produce efficient machine code.

11.3 Some beneficial advice

2 2 [suggestion

11-3-1

】 Be careful to write errors when the 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.

2 2 [suggestion

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.

2 2 [suggestion

11-3-3

】 Beware of the initial value of the variable, the default value is incorrect, or the accuracy is not enough.

2 2 [suggestion

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.

2 2 [suggestion

11-3-5

"Beware of the bids of the overflow or underflow, array.

2 2 [suggestion

11-3-6

】 Be careful to write the wrong handler, and the heart error handler itself is incorrect.

2 2 [suggestion

11-3-7

】 Beware of file I / O errors.

2 2 [suggestion

11-3-8

】 Avoid writing skills with high code.

2 2 [suggestion

11-3-9

】 Do not design, very flexible data structure.

2 2 [suggestion

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.

2 2 [suggestion

11-3-11

】 Try to use the standard library function, do not "invent" existing library functions.

2 2 [suggestion

11-3-12

】 Try not to use variables close to specific hardware or software environment.

2 2 [suggestion

11-3-13

】 Set the selection of the compiler to the most stringent state.

2 2 [suggestion

11-3-14

】 If possible, use PC-LINT, LOGISCOPE and other tools to conduct 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, etc.), Machinery Industry Press, 2000 [MAGUIR] 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

File structure

importance

Review item

in conclusion

Is 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?

important

Do IFNDEF / DEFINE / ENDIF preparation block?

Whether to store "declaration" in the header file without storing "definition"

......

Program

importance

Review item

in conclusion

Is the air line worth it?

Is the space in the code line be decent?

Is the long line split?

Does "{" and "}" each occupied and aligned with the same column?

important

Does a line of code do only one thing? If you only define a variable, you only write a statement.

important

IF, for, while, do, other words,, regardless of "{}", regardless of how much the execution statement is executed.

important

Does the modifier * and & & climb the variable name when defining a variable (or parameter)?

Whether the comment is clear and necessary?

important

Note Is there errors or possible misunderstandings?

important

Whether the PUBLIC, PROTECTED, and PRIVATE sequence of class structure is consistent in all programs?

... Naming Rules

importance

Review item

in conclusion

important

Is the naming rule consistent with the style of the operating system or development tool used?

Is the identifier intuitive and can you spell?

The length of the identifier should comply with the principle of "min-length &&max-information?

important

Does the same local variable and all variables in the program?

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 item

in conclusion

important

If the operator in the code line is more, whether it has been clearly determined by parentheses?

Whether to write too complicated or multi-purpose composite expressions?

important

Do you confuse composite expressions with "real mathematical expressions"?

important

Whether to write if statements in a hidden error? For example

(1) Compare the Boolean variable directly with 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?

important

Does the CASE statement have forgotten add Break?

important

Do you forget to write Switch's Default branch? important

Is there a hidden danger when using a GOTO statement? For example, skip the construction of certain objects, the initialization of variables, important calculations, etc.

......

constant

importance

Review item

in conclusion

Whether to use meanly intuitive constants 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.

......

Function design

importance

Review item

in conclusion

Is the writing of the parameters 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?

important

Do you return a normal value and the error flag to return? The normal value should be obtained with the output parameters, and the error flag returns with the return statement.

important

Do you check the validity of the parameters in the "entrance" of the function body.

important

Use abuse assert? For example, confusion illegal situations and errors, the latter inevitably exist and must be processed.

important

Does the RETURN 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. "Use Const WHENEVER You NEED"

......

Memory management

importance

Review item

in conclusion

important

Is it necessary to check if the pointer value is NULL when applying for memory with Malloc or New (Preventing memory that is NULL)

important

Do you forget the initial value of array and dynamic memory? (Prevent memory that will not be initialized as right value)

important

Is the subscript of array or pointer?

important

Dynamic memory application and release is pairing? (Prevent memory leakage)

important

Does it effectively handle the "memory consumption" problem?

important

Do you modify the contents of "pointing to constants"?

important

Do you have a wild pointer? E.g

(1) The pointer variable is not initialized.

(2) After using Free or Delete released the memory, forget to set the pointer to NULL.

important

Do Malloc / Free and New / DELETE confused?

important

Is the 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 C functions

importance

Review item

in conclusion

Does the overload function have an amphony?

important

Is it confused with the overloading of the member function, coverage and hidden?

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

Is it replaced with the inner function?

......

Class constructor, destructor, and assignment function

importance

Review item

in conclusion

important

Whether to make the programming specification, the C compiler automatically generates four default functions: (1) Default non-parameter constructor; (2) default copy constructor; (3) default destructor (4) Default assignment function. important

Do you miss some initialization work in the constructor?

important

Does the initialization table of constructor correctly use it correctly?

important

Are some cleared work in the 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 resources; (3) Assign new memory resources and copy content; (4) Return * this. Will I miss important steps?

important

Does the derived structure function, destructive function, assignment function correctly correctly wrote? Precautions:

(1) Detective class cannot inherit the construction function, sector function, and assignment function of the base class.

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

......

Advanced features of class

importance

Review item

in conclusion

important

Whether it violates the rules of inheritance and combination?

(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 common problems

importance

Review item

in conclusion

important

Data type problem:

(1) Is there any error in the data type of the variable?

(2) Is there a value of different data types?

(3) Is there a comparison of different data types?

important

Variable value:

(1) Is there any error in the initialization or default value of the variable?

(2) Is the variable overflow or overflow?

(3) Is the value of the variable enough?

important

Logical judgment problem:

(1) Is the relatively ineffective due to accuracy?

(2) Is the priority in the expression incorrect?

(3) Is the logic judgment reversed?

important

Cycle problem:

(1) Is the loop termination condition incorrect?

(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 do wrong 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

File 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

{

IF (condition)

DOSMETHING ();

Else

Doothershing ();

}

// the second

IF (condition)

{

For (i = 0; i

DOSMETHING ();

}

Else

{

For (i = 0; i

}

advantage:

Disadvantages:

advantage:

Disadvantages:

4. Thoughts on memory (20 points)

Void getMemory (char * p)

{

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: char * getMemory (void)

{

Char p [] = "Hello World";

Return P;

}

Void test (void)

{

CHAR * STR = NULL;

Str = getMemory ();

Printf (STR);

}

What kind of results will there be a Test 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?

A: 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?

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); // Destructor

String & Operate = (const string & other); // assignment function

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, no 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 <= epinon)

You should try to translate into "> =" or "<=" in such forms with "==" or "! =" And digital comparisons.

The following is the wrong way of writing, not 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, not 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; calculates sizeof (STR) = 6 (2 points)

SizeOf (P) = 4 (2 points)

SIZEOF (N) = 4 (2 points) 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 for FileName.h from the standard library path

For #include "filename.h", the compiler starts searching from the user's work path to search for filename.h

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

A: (1) You can define constants

(2) const can modify the parameters, return values, and even the definition body 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) Answer: C language support function overload, 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. Suppose a function of a function is: Void foo (int X, int y);

This function is compiled by the C compiler to the name _foo, and 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

{

IF (condition)

DOSMETHING ();

Else

Doothershing ();

}

IF (condition)

{

For (i = 0; i

DOSMETHING ();

}

Else

{

For (i = 0; i

Doothershing ();

}

Advantages: the program is concise

Disadvantages: N-1 logic judgment, and interrupt the loop "pipeline" job so that the compiler cannot optimize the loop, reducing efficiency.

Advantages: high efficiency of cycles

Disadvantages: the program is not simple

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

Void getMemory (char * p)

{

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";

Return P;

}

Void test (void)

{

CHAR * STR = NULL;

Str = getMemory ();

Printf (STR);

}

What kind of results will there be a Test function?

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) Ability to output Hello

(2) Memory leakage

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); after, 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) && (strsrc! = null); // 2 points

Char * address = strDest; // 2 points

While (* strDest = * strsrc )! = '/

0

'

) // 2 minutes

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); // Destructor

String & Operate = (const string & other); // assignment function

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, it is better.

* m_data = '/

0

'

;

}

Else

{

INT length = Strlen (STR);

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

STRCPY (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 the 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 the reference / 3 points of this object

Return * this;

}

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

New Post(0)