Programming Practices ==Avoid Global Functions, Data and Classes === One way to avoid global functions and classes. Example:
Placement of DeclarationsLocal variables can be declared at the start of the function, at the start of a conditional block, or at the point of first use. However, declaring within a conditional block or at the point of first use may yield a performance advantage, since memory allocation, constructors, or class loading will not be performed at all if those statements are not reached. Switch StatementsSpecify a break statement after every case block, including the last one unless multiple labels are used for one selection of code. Always define the default case. Return StatementsWhere practical, there will be only one return from a function or method as the last statement. Otherwise, the number of returns will be minimized. Highlight returns with comments and/or blank lines to keep them from being lost in other code. Other Flow Control Statements* Never use goto. * The choice of loop construct (for, while or do-while) should depend on the specific use of the loop. * Always use unsigned for variables which cannot reasonably have negative values. * Always use inclusive lower limits and exclusive upper limits in conditionals. * Avoid the use of continue. * Use break to exit a loop if this avoids the use of flags. ExpressionsAlways use parenthesis to clarify the order of evaluation when three or more operators are used. CastsAvoid the use of casts except where unavoidable, since this can introduce run-time bugs by defeating compiler type-checking. Working with third-party libraries often requires the use of casts. When you need to cast, document the reasons. Do not write code which depends on functions that use implicit type conversions. Never convert pointers to objects of a derived class to pointers to objects of a virtual base class. Never convert a const to a non-const. LiteralsConstants are to be defined using const or enum; never using #define. Avoid the use of numeric values in code; use symbolic values instead. Example:
Explicit InitializationIn general, explicitly initialize all variables before use. Always initialize all pointers either to zero or to an object. Set pointers to zero after delete. Constructs to Avoid* Inlines are preferred over #define macros * The use of typedef is discouraged when actual types such as class, struct, or enum would be a better choice. * Avoid side-effects! They make the code harder to read and the compiler is smarter than you are. Example:
MacrosIf macros must be used, they should be enclosed in parentheses to eliminate ambiguity on expansion. Example:
Debug Compile-time SwitchCode used only during development for debugging or performance monitoring should be conditionally compiled using #ifdef compile-time switches. The symbols used are DEBUG and STATS, respectively. DEBUG statements announcing entry into a function or member function should provide the entire function name including the class. Example:
Memory ManagementAlways use new and delete instead of malloc/calloc/realloc and free. Allocate memory with new only when necessary for variable to remain after leaving the current scope. Always use the delete[] operator to deallocate arrays. After deletion, set the pointer to zero, to safeguard possible future calls to delete. Required Member Functions* Default Constructor * Virtual Destructor * Copy Constructor * Assignment Operator Class ConstructorsAll constructors should initialize all member variables to a known state. This implies that all classes should have a default constructor (i.e., <nop>MyClass? ();) defined. Providing a deep copy constructor is strongly recommended. If the programmer wishes to not fully implement a copy constructor, then a stub copy constructor should be written and placed in the private section so no one will accidentally call it. A class which uses "new" to allocate instances managed by the class, must define a copy constructor. Do not do any real work in an object's constructor. Inside a constructor initialize variables only and perform only actions that can't fail. Class DestructorsAll classes which allocate resources which are not automatically freed should have a destructor which explicitly frees the resources. Since any class may someday be used as a base class, destructors should be declared virtual, even if empty. Argument PassingIf the argument is small and will not be modified, use the default pass by value. If the argument is large and will not be modified, pass by const reference. If the argument will be modified, pass by reference. Example:
Default ArgumentsWhere possible, use default arguments instead of function overloading to reduce code duplication and complexity. Overriding Virtual FunctionsWhen overriding virtual functions in a new subclass, explicitly declare the functions virtual. Although not normally required by the compiler, this aids maintainability by making clear that the function is virtual without having to refer to the base class header file. Function ReentrancyFunctions should not keep static variables that prevent a function from being reentrant. Functions can declare variables static. Problems happen when the function is called one or more times at the same time. This can happen when multiple threads are used or from a signal handler. Using the static buffer caused results to overlap and become corrupted. Const Member FunctionsA member function that does not affect the state of an object (its instance variables) is to be declared const. If the behavior of an object is dependent on data outside the object, this data is not to be modified by const member functions. Example:
This allows these functions to be called for objects which were either declared as const or passed as const arguments. It is recommended that all member function parameters be declared const, if at all possible. Example:
Member Function Return TypesA public member function must never return a non-const reference or pointer to member data. A public member function must never return a non-const reference or pointer to data outside an object, unless the object shares the data with other objects. By allowing a user direct access to the private member data of an object, this data may be changed in ways not intended. Referencing Non-C++ FunctionsUse the extern "C" mechanism to allow access to non-C++ (not just C) functions. This mechanism disables C++ name mangling, which allows the linker to resolve the function references. Example:
NULL PointerUse the number zero (0) instead of the NULL macro for initialization, assignment, and comparison of pointers. The use of NULL is not portable. Cast when appropriate (e.g., (char*)0). Enumerated TypesUse enumerated types instead of numeric codes. Enumerations improve robustness by allowing the compiler to perform type-checking, and are more readable and maintainable. Terminating Stream OutputUse the iostream manipulator endl to terminate an output line, instead of the newline character \n or \n\r. In addition to being more readable, the endl manipulator flushes the output buffer and automatically performs platform dependent linefeed/carriage return translation. Object InstantiationWhere possible, move object declarations and instantiations out of loops, using assignment to change the state of the object at each iteration. This minimizes overhead due to memory allocation from the heap. EncapsulationInstance attributes of a class never be declared public. Open access to internal variables exposes structure and does not allow methods to assume values are valid. Putting variables in the private section is preferable over the protected section, for more complete encapsulation. Use get and set methods in either protected or public if needed. MiscellaneousTo determine to complexity of a routine roughly, hand calculate the Decision Point number. Decision points are calculated using the following formula: * Start with 1 for straight path through routine * Add 1 for each keyword (if, while, do, repeat, for, and, or) * Add 1 for each case in a case statement, plus 1 for default A routine with a Decision Point number greater than a 10 score may be too complicated and may need to be refactored. Portability Concerns* Avoid the direct use of pre-defined data types in declarations. * Do not assume that an int and a long have the same size. * Do not assume that an int is 32 bits long (it may be 64 bits long). * Do not assume that a char is signed or unsigned. * Always set char to unsigned if 8-bit ASCII is used. * Be careful not to make type conversions from a `shorter' type to a `longer' one. * Do not assume that pointers and integers have the same size. * Use explicit type conversions for arithmetic using signed and unsigned values. * Do not assume that you know how an instance of a data type is represented in memory. * Do not assume that longs, floats, doubles or long doubles may begin at arbitrary addresses. * Do not depend on underflow or overflow functioning in any special way. * Do not assume that the operands in an expression are evaluated in a definite order. * Do not assume that you know how the invocation mechanism for a function is implemented. * Do not assume that an object is initialized in any special order in constructors. * Do not assume that static objects are initialized in any special order. * Do not write code which is dependent on the lifetime of a temporary object. * Avoid using shift operations instead of arithmetic operations. * Avoid pointer arithmetic. Error CheckingCheck every system call for an error return, unless you know you wish to ignore errors. Include the system error text and error numbers, if available, for every system error message. |
Programming Practices ==Avoid Global Functions, Data and Classes === One way to avoid global functions and classes. Example:
Placement of DeclarationsLocal variables can be declared at the start of the function, at the start of a conditional block, or at the point of first use. However, declaring within a conditional block or at the point of first use may yield a performance advantage, since memory allocation, constructors, or class loading will not be performed at all if those statements are not reached. Switch StatementsSpecify a break statement after every case block, including the last one unless multiple labels are used for one selection of code. Always define the default case. Return StatementsWhere practical, there will be only one return from a function or method as the last statement. Otherwise, the number of returns will be minimized. Highlight returns with comments and/or blank lines to keep them from being lost in other code. Other Flow Control Statements* Never use goto. * The choice of loop construct (for, while or do-while) should depend on the specific use of the loop. * Always use unsigned for variables which cannot reasonably have negative values. * Always use inclusive lower limits and exclusive upper limits in conditionals. * Avoid the use of continue. * Use break to exit a loop if this avoids the use of flags. ExpressionsAlways use parenthesis to clarify the order of evaluation when three or more operators are used. CastsAvoid the use of casts except where unavoidable, since this can introduce run-time bugs by defeating compiler type-checking. Working with third-party libraries often requires the use of casts. When you need to cast, document the reasons. Do not write code which depends on functions that use implicit type conversions. Never convert pointers to objects of a derived class to pointers to objects of a virtual base class. Never convert a const to a non-const. LiteralsConstants are to be defined using const or enum; never using #define. Avoid the use of numeric values in code; use symbolic values instead. Example:
Explicit InitializationIn general, explicitly initialize all variables before use. Always initialize all pointers either to zero or to an object. Set pointers to zero after delete. Constructs to Avoid* Inlines are preferred over #define macros * The use of typedef is discouraged when actual types such as class, struct, or enum would be a better choice. * Avoid side-effects! They make the code harder to read and the compiler is smarter than you are. Example:
MacrosIf macros must be used, they should be enclosed in parentheses to eliminate ambiguity on expansion. Example:
Debug Compile-time SwitchCode used only during development for debugging or performance monitoring should be conditionally compiled using #ifdef compile-time switches. The symbols used are DEBUG and STATS, respectively. DEBUG statements announcing entry into a function or member function should provide the entire function name including the class. Example:
Memory ManagementAlways use new and delete instead of malloc/calloc/realloc and free. Allocate memory with new only when necessary for variable to remain after leaving the current scope. Always use the delete[] operator to deallocate arrays. After deletion, set the pointer to zero, to safeguard possible future calls to delete. Required Member Functions* Default Constructor * Virtual Destructor * Copy Constructor * Assignment Operator Class ConstructorsAll constructors should initialize all member variables to a known state. This implies that all classes should have a default constructor (i.e., <nop>MyClass? ();) defined. Providing a deep copy constructor is strongly recommended. If the programmer wishes to not fully implement a copy constructor, then a stub copy constructor should be written and placed in the private section so no one will accidentally call it. A class which uses "new" to allocate instances managed by the class, must define a copy constructor. Do not do any real work in an object's constructor. Inside a constructor initialize variables only and perform only actions that can't fail. Class DestructorsAll classes which allocate resources which are not automatically freed should have a destructor which explicitly frees the resources. Since any class may someday be used as a base class, destructors should be declared virtual, even if empty. Argument PassingIf the argument is small and will not be modified, use the default pass by value. If the argument is large and will not be modified, pass by const reference. If the argument will be modified, pass by reference. Example:
Default ArgumentsWhere possible, use default arguments instead of function overloading to reduce code duplication and complexity. Overriding Virtual FunctionsWhen overriding virtual functions in a new subclass, explicitly declare the functions virtual. Although not normally required by the compiler, this aids maintainability by making clear that the function is virtual without having to refer to the base class header file. Function ReentrancyFunctions should not keep static variables that prevent a function from being reentrant. Functions can declare variables static. Problems happen when the function is called one or more times at the same time. This can happen when multiple threads are used or from a signal handler. Using the static buffer caused results to overlap and become corrupted. Const Member FunctionsA member function that does not affect the state of an object (its instance variables) is to be declared const. If the behavior of an object is dependent on data outside the object, this data is not to be modified by const member functions. Example:
This allows these functions to be called for objects which were either declared as const or passed as const arguments. It is recommended that all member function parameters be declared const, if at all possible. Example:
Member Function Return TypesA public member function must never return a non-const reference or pointer to member data. A public member function must never return a non-const reference or pointer to data outside an object, unless the object shares the data with other objects. By allowing a user direct access to the private member data of an object, this data may be changed in ways not intended. Referencing Non-C++ FunctionsUse the extern "C" mechanism to allow access to non-C++ (not just C) functions. This mechanism disables C++ name mangling, which allows the linker to resolve the function references. Example:
NULL PointerUse the number zero (0) instead of the NULL macro for initialization, assignment, and comparison of pointers. The use of NULL is not portable. Cast when appropriate (e.g., (char*)0). Enumerated TypesUse enumerated types instead of numeric codes. Enumerations improve robustness by allowing the compiler to perform type-checking, and are more readable and maintainable. Terminating Stream OutputUse the iostream manipulator endl to terminate an output line, instead of the newline character \n or \n\r. In addition to being more readable, the endl manipulator flushes the output buffer and automatically performs platform dependent linefeed/carriage return translation. Object InstantiationWhere possible, move object declarations and instantiations out of loops, using assignment to change the state of the object at each iteration. This minimizes overhead due to memory allocation from the heap. EncapsulationInstance attributes of a class never be declared public. Open access to internal variables exposes structure and does not allow methods to assume values are valid. Putting variables in the private section is preferable over the protected section, for more complete encapsulation. Use get and set methods in either protected or public if needed. MiscellaneousTo determine to complexity of a routine roughly, hand calculate the Decision Point number. Decision points are calculated using the following formula: * Start with 1 for straight path through routine * Add 1 for each keyword (if, while, do, repeat, for, and, or) * Add 1 for each case in a case statement, plus 1 for default A routine with a Decision Point number greater than a 10 score may be too complicated and may need to be refactored. Portability Concerns* Avoid the direct use of pre-defined data types in declarations. * Do not assume that an int and a long have the same size. * Do not assume that an int is 32 bits long (it may be 64 bits long). * Do not assume that a char is signed or unsigned. * Always set char to unsigned if 8-bit ASCII is used. * Be careful not to make type conversions from a `shorter' type to a `longer' one. * Do not assume that pointers and integers have the same size. * Use explicit type conversions for arithmetic using signed and unsigned values. * Do not assume that you know how an instance of a data type is represented in memory. * Do not assume that longs, floats, doubles or long doubles may begin at arbitrary addresses. * Do not depend on underflow or overflow functioning in any special way. * Do not assume that the operands in an expression are evaluated in a definite order. * Do not assume that you know how the invocation mechanism for a function is implemented. * Do not assume that an object is initialized in any special order in constructors. * Do not assume that static objects are initialized in any special order. * Do not write code which is dependent on the lifetime of a temporary object. * Avoid using shift operations instead of arithmetic operations. * Avoid pointer arithmetic. Error CheckingCheck every system call for an error return, unless you know you wish to ignore errors. Include the system error text and error numbers, if available, for every system error message. |
One way to avoid global functions and classes.
Example:
// Instead of declaring: void Emc2_myFunc1(); void Emc2_myFunc2(); class Emc2MyClass { /* ... */ }; // Encapsulate the functions using an abstract class: class Emc2 { public: static void MyFunc1(); static void MyFunc2(); class MyClass { /* ... */ }; private: virtual Dummy() = 0; // Make the class abstract }; // Now, functions and classes are accessed by the scope-operator: Emc2::MyFunc1(); Emc2::MyFunc2(); Emc2::MyClass my_object;
Specify a break statement after every case block, including the last one unless multiple labels are used for one selection of code. Always define the default case.
Where practical, there will be only one return from a function or method as the last statement. Otherwise, the number of returns will be minimized. Highlight returns with comments and/or blank lines to keep them from being lost in other code.
Always use parenthesis to clarify the order of evaluation when three or more operators are used.
Avoid the use of casts except where unavoidable, since this can introduce run-time bugs by defeating compiler type-checking. Working with third-party libraries often requires the use of casts. When you need to cast, document the reasons. Do not write code which depends on functions that use implicit type conversions. Never convert pointers to objects of a derived class to pointers to objects of a virtual base class. Never convert a const to a non-const.
Constants are to be defined using const or enum; never using #define. Avoid the use of numeric values in code; use symbolic values instead.
Example:
const double PI = 3.141259; // right const char APP_NAME = "ACME Spreadsheet 1.0"; // right area = 3.141259 * radius * radius; // not recommended cout << "ACME Spreadsheet 1.0" << endl; // not recommended
In general, explicitly initialize all variables before use. Always initialize all pointers either to zero or to an object. Set pointers to zero after delete.
Example:
if ( p_record = get_record()) // This saves you nothing at all int i = 0 while( i < 10 ) { cout << i++ << endl; // Use a for loop or put i++ on another line }
If macros must be used, they should be enclosed in parentheses to eliminate ambiguity on expansion.
Example:
#define MAX( x, y ) ( (x) > (y) ) ? (x) : (y) )
Code used only during development for debugging or performance monitoring should be conditionally compiled using #ifdef compile-time switches. The symbols used are DEBUG and STATS, respectively. DEBUG statements announcing entry into a function or member function should provide the entire function name including the class.
Example:
#ifdef DEBUG cout << "ClassName::MemberName: about to do something" << endl; #endif
Always use new and delete instead of malloc/calloc/realloc and free. Allocate memory with new only when necessary for variable to remain after leaving the current scope. Always use the delete[] operator to deallocate arrays. After deletion, set the pointer to zero, to safeguard possible future calls to delete.
All constructors should initialize all member variables to a known state. This implies that all classes should have a default constructor (i.e., <nop>MyClass? ();) defined. Providing a deep copy constructor is strongly recommended. If the programmer wishes to not fully implement a copy constructor, then a stub copy constructor should be written and placed in the private section so no one will accidentally call it. A class which uses "new" to allocate instances managed by the class, must define a copy constructor. Do not do any real work in an object's constructor. Inside a constructor initialize variables only and perform only actions that can't fail.
All classes which allocate resources which are not automatically freed should have a destructor which explicitly frees the resources. Since any class may someday be used as a base class, destructors should be declared virtual, even if empty.
If the argument is small and will not be modified, use the default pass by value. If the argument is large and will not be modified, pass by const reference. If the argument will be modified, pass by reference.
Example:
void A::function( int not_changed ); // default: pass by value void B::function( const C& r_big_object ) // pass by const reference void C::function( int& r_result ); // pass by reference
Where possible, use default arguments instead of function overloading to reduce code duplication and complexity.
When overriding virtual functions in a new subclass, explicitly declare the functions virtual. Although not normally required by the compiler, this aids maintainability by making clear that the function is virtual without having to refer to the base class header file.
Functions should not keep static variables that prevent a function from being reentrant. Functions can declare variables static. Problems happen when the function is called one or more times at the same time. This can happen when multiple threads are used or from a signal handler. Using the static buffer caused results to overlap and become corrupted.
A member function that does not affect the state of an object (its instance variables) is to be declared const. If the behavior of an object is dependent on data outside the object, this data is not to be modified by const member functions.
Example:
func(...) const { code here }
This allows these functions to be called for objects which were either declared as const or passed as const arguments.
It is recommended that all member function parameters be declared const, if at all possible.
Example:
MyFunc(const int i) { some code }
A public member function must never return a non-const reference or pointer to member data. A public member function must never return a non-const reference or pointer to data outside an object, unless the object shares the data with other objects. By allowing a user direct access to the private member data of an object, this data may be changed in ways not intended.
Use the extern "C" mechanism to allow access to non-C++ (not just C) functions. This mechanism disables C++ name mangling, which allows the linker to resolve the function references.
Example:
extern "C" { void a_function(); // single non-C++ function prototype } extern "C" { #include "functions.h" // library of non-C++ functions }
Use the number zero (0) instead of the NULL macro for initialization, assignment, and comparison of pointers. The use of NULL is not portable. Cast when appropriate (e.g., (char*)0).
Use enumerated types instead of numeric codes. Enumerations improve robustness by allowing the compiler to perform type-checking, and are more readable and maintainable.
Use the iostream manipulator endl to terminate an output line, instead of the newline character \n or \n\r. In addition to being more readable, the endl manipulator flushes the output buffer and automatically performs platform dependent linefeed/carriage return translation.
Where possible, move object declarations and instantiations out of loops, using assignment to change the state of the object at each iteration. This minimizes overhead due to memory allocation from the heap.
Instance attributes of a class never be declared public. Open access to internal variables exposes structure and does not allow methods to assume values are valid. Putting variables in the private section is preferable over the protected section, for more complete encapsulation. Use get and set methods in either protected or public if needed.
To determine to complexity of a routine roughly, hand calculate the Decision Point number. Decision points are calculated using the following formula:
A routine with a Decision Point number greater than a 10 score may be too complicated and may need to be refactored.
Check every system call for an error return, unless you know you wish to ignore errors. Include the system error text and error numbers, if available, for every system error message.