C Best Coding Practice
With the development of computer languages, we are more and more easy to write a program now. Using some software development tools, you often use the dragotic point of the mouse, the computer will automatically generate a lot of code. But in many cases, this ability of the computer is abused, and we often consider grab this program, not to consider how the program is performance, whether the program is sufficient. The purpose of this lesson is mainly introduced some of the experience of coding, so that everyone prepared more robust and high performance.
1, Prefer const and inline to #define
In C programming should try to use const and inline to replace #define, try to do it without #define. #DEfine common use has "defined constants" and "definition macro", but there are many disadvantages.
First, the error is not intuitive, which is not conducive to debugging. Define definitions are processed by the pre-processing program, which is completely text replacement, and do not do any type check. In the compiler processing phase, the definition definitions have been fully replaced, so that any relevant information is not seen when Debug, that is, the STEP INTO macro is not tracked. For example, use the define to define 1.653 with define, and the compiler can not see the name of Aspect_Ratio. If the compiler reports 1.653 wrong, then it is from knowing this 1.653. You should use the following statement when you really encode:
Static const Double aspect_ratio = 1.653;
Second, there is no type of information, not Type Safe. Because it is a replacement of the text level, this is not conducive to the maintenance of the program.
Third, Define is easy to cause pollution. For example, if there are two headers that define aspect_ratio, and a CPP file contains these two headers at the same time, then conflicts. It is more difficult to check another error, such as the following code:
// in Header File Def.h
#define apple 1
#define
Orange
2
#define PineApple 3
...
// in Some CPP file what incruDes the def.h
ENUM colors {White, Black, Purple,
Orange
}
In the .h file, Orange is defined as a fruit, and Orange in the .CPP file has become a color, then the compiler will replace the ORANGE here to 2, and the compilation may still pass, the program is also It is possible to run, but this has become a bug, showing a weird mistake, and it is difficult to find the error. For example, a macro that defines a large number A and B, # define max (a, b) ((a)> (b)? (A): (b))
INT A = 5, b = 0;
MAX ( A, B);
MAX ( A, B 10);
In the above operation, Max ( a, b); in the statement, a twice, while Max ( A, B 10); in the statement, a only once, so in program processing It is very likely to be a bug, and this BUG is also very difficult. You can use the following statement when you actually encode:
Template
Inline const T &
Max (Const T & A, Const T & B) {RETURN A> B? A: B;}
2, Prefer C - Style Casts
In the program, you will need to convert a type to another type. In C , Static_cast, const_cast, dynamic_cast, reinterpret_cast keyword should be used to do type conversion. Because this has the following benefits, one is that it is an annotation, seeing these keywords in the code, you can know the type conversion. Second, the type conversion in the C language is usually difficult to search, and the keyword CAST can easily find the type conversion in the program.
3, DISTINGUISH BETWEEN Prefix and Postfix Forms of Increment and Decrement Operators
Usually the type of the operating system or the compiler itself, the prefix (prefix, such as i) is the same as Postfix (suffix, such as i ). Because the current compilers are very smart, it will automatically optimize, the assembly code of these is the same, and performance will not have different. But sometimes there will be different, such as some types of operators. Below is an operation procedure to simulate Prefix and Postfix, you can find a temporary variable in Postfix operation, and this temporary variable is an additional time and overhead.
// prefix form: increment and fetch
UPINT & UPINT :: Operator ()
{
* this = 1; // increment
Return * this; // fetch
}
// Postfix Form: fetch and increment
Const Upint Upint :: Operator (INT)
{
UPINT OLDVALUE = * this; // fetch
(* this); // increment
Return OldValue; // Return What WAS Fetched
}
Under normal circumstances, it is not necessary to distinguish or after , but we best read it in the form of i when writing programs, such as using Iterator in STL, prefix and postfix There will be considerable differences in performance. Don't underestimate these details, don't pay attention to specific details when writing programs, you will find that the performance of the program will be very low. However, it should be noted that although in most cases can be used instead of Postfix, there is a case exception, that is, when there is a [] operator, such as GzaRray [ index] is not equal to GzaRray [INDEX ].
4, Minimizing Compile-Time Dependencies
Some people often like to contain one .h file to another .h file, and practice prove that this is a very bad habit when doing large software, because this will cause a lot of dependencies, including more Many.h files, others use this class, and there may not be these .h files in his project, which is likely to compile. And do this, it is also possible to make it difficult to update a module. Because one .h file is included in many modules, if this .h file is modified, when compiling the system, the compiler will find which modules depend on a modified .h file, then therefore causes all included The modules of this .h file must be recompiled. When the project is relatively small, everyone may still feel the difference, but if it is in a large software system, you may compile the source code to seven or eight hours. If this .h file is included in many modules, even if you add a row of comments in .h file, check the compiler to check which files are changed, then all modules that contain this .h file will be recompiled, resulting Huge time and energy burden. For this problem, the solution is to let .h files are included, that is, let it contain as few things. The so-called less as little refers to any one of the .h files that it contains it, it will not work properly. In fact, in many cases, it does not need one .h file to contain another .h file, you can solve this problem with dependencies through the Class declaration. Let's see the following example: #include "a.h" // Class A
#include "b.h" // Class B
#include "c.h" // Class C
#include "d.h" // Class D
#include "e.h" // Class E
Class x: Public A, Private B
{
PUBLIC:
E
SomefunctionCall
(E someparameter);
Private:
D M_DINSTANCE;
}
When class X is derived from class A and class B, you need to know which DATAs in the memory are usually used in memory, and it is often the DATA of the base class in front. It is followed by the DATA defined by this part. Therefore It must be known that the internal details of class A and class B, otherwise the compiler will not be able to schedule memory. However, when processing parameters and parameter return values, do not need to know this, some of the SomeFunctionCall () defined here is enough to know that E is a class, do not need to know the DATA in class E, etc. Details of the details. The above code should be rewritten into the following form to reduce dependencies:
#include "a.h" // Class A
#include "b.h" // Class B
#include "c.h" // Class C
#include "d.h" // Class D
Class E;
Class x: Public A, Private B
{
PUBLIC:
E
SomefunctionCall
(E someparameter);
Private:
D M_DINSTANCE;
}
5, Never Treat Arrays Polymorphically
Don't use the arrays and polymorphisms, please see the example below.
Class BST {...};
Class BalancedBST: Public BST {...};
Void PrintBSTARRAY (Ostream & S, Const Bst Array [], INT Numelements) {
For (INT i = 0; i { S << array [i]; // this assumes an operator << is defined for BST } } Balancedbst bbstarray [10]; PrintBSTARRAY (COUT, BBSTARRAY, 10); Array is a continuous memory space in memory, and how should I locate an element in an array? The process is such that the compiler can know the length of each data type, if index of the array is 0, will automatically take the first element; if it is specified an index, the compiler will be based on this index The length of the data type automatically calculates the location of the element. In the PrintBSTARRAY () function, although the incoming parameters are the BalanceBST type, because the type defined by the original is BST, it will still calculate the length of the type according to BST. And usually the memory of the derivative instance is larger than the memory occupied by the base class instance, so the program will report an error in compile. Keep in mind, never use the polymorphism of the array and C . 6, Prevent Exceptions from Leaving Destructors Do not throw an exception in the destructive function. There are usually two situations that cause the destructuring function to call, one is when the object of this class leaves its domain, or a pointer to a class object in the delete expression, and the other is due to abnormal sectors. Call. If the destructor is called due to Exception, it is throws an exception in the destructive function, and the program will be terminated immediately, or even the memory is released. Therefore, if you throw an abnormality in the destructor, it is easy to confuse the cause of an abnormal, and such software will make the user very annoyed. Since some other functions are likely to call other functions, it is important to pay attention to whether or not they will throw an exception to these functions. If the words, it must be careful. For example, the following code: Session :: ~ session () { LogDestruction (this); } For example, the logDestruction () function may throw an exception, then we should use the following code: Session :: ~ session () { Try { LogDestruction (this); } Catch (...) { } } This will not be turned off immediately, you can give some other options, at least let him save the work currently do. 7, Optimization: Remember the 80-20 rule There is a 20-80 rule in the software industry, in fact, this is a very interesting phenomenon, such as 20% of the code in a program uses 80% of the resources occupying the program; 20% of the code in a program occupies the total runtime 80%; 20% of the code in a program uses 80% of the memory in the program; 80% maintenance force is required above 20% of the code, and so on. This law can also be continued to be promoted, but this rule cannot be proven, it is the result of people observed in practice. From this law, we have targeted when we do programs optimization. For example, if you want to improve the running speed of the code, you can know that 20% of the code take up 80% of the run time according to this rule, so we will find the 20% code and make the corresponding optimization, then our program is running speed. There is a big increase. If there is a function, the run time of the program is occupied, and if the execution speed of this function is 10 times, the improving the overall performance of the program is very huge. If there is a function of running only 1% of the total time, it will increase the running speed of this function to 1000 times, and the improvement of the overall performance of the program is not much affected. So our basic idea is to find the function of the maximum running time, then optimize it, even if it is only improved, the overall performance of the program can also be improved. To find the 20% code, our method is to use Profiler, which is actually a tool developed by companies, and can check the usage of the memory allocated in the program, and the time running every function, etc. . Common Profiler has Visual Studio Profiler, which developed by Intel, Visual Studio Profiler, DEVPartner from Compuware, etc.