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.