32
C++ Programming Standards Table Of Contents Memory management (New and Delete)............................1 Operator new and operator delete............................1 Cautions.................................................... 1 Allocation Failures......................................... 1 Arrays...................................................... 1 Pointers.................................................... 2 Data Type Considerations......................................3 Casts or Type Conversions...................................3 #define..................................................... 3 ENUM........................................................ 3 References (&).............................................. 4 Pointers (*)................................................ 4 Function Pointers........................................... 4 Typedef..................................................... 5 INT......................................................... 5 CHAR........................................................ 5 LONG........................................................ 6 SHORT....................................................... 6 Foundation Large INT........................................ 6 Foundation String and Foundation String Variable............6 Collections................................................. 6 Data Sources................................................ 7 Structures.................................................. 7 Global Data...................................................7 Location......................................................7 Section 1 - Classes...........................................7 Components of a Class.........................................9 Header File................................................. 9 Declaration of Class Functions and Data......................10 Public..................................................... 10 Protected.................................................. 10 Private.................................................... 11 Inheritance................................................ 11 Includes.....................................................12 Format of Include Statement................................12 i

C Standards

Embed Size (px)

DESCRIPTION

C and C++ standards

Citation preview

Table Of Contents

Memory management (New and Delete)1Operator new and operator delete1Cautions1Allocation Failures1Arrays1Pointers2Data Type Considerations3Casts or Type Conversions3#define3ENUM3References (&)4Pointers (*)4Function Pointers4Typedef5INT5CHAR5LONG6SHORT6Foundation Large INT6Foundation String and Foundation String Variable6Collections6Data Sources7Structures7Global Data7Location7Section 1- Classes7Components of a Class9Header File9Declaration of Class Functions and Data10Public10Protected10Private11Inheritance11Includes12Format of Include Statement12Which files to Include12Preventing Multiple Inclusion of Headers with #define12Contents of the Include File13Constructor Considerations14Initialization Order14Indicating Status of Instantiated Objects14Copy Constructors16Destructor Considerations17Required Functions18Naming18Inline Functions19Templates19Basic Elements of Functions19Function Recommendations27Specific Types of Functions28Friend29Non-Portable Classes29New Operator29Constructor29

C++ Programming Standards

1

7-96S:\Guide To The Galaxy\Documents\BASE24-Es C++ Programming Standards V23.Doc

ACI WorldwideFor Internal Use Only2

Memory management (New and Delete)Operator new and operator deleteAlways use the same form of new and delete.string *stringPtr1 = new string;string *stringPtr2 = new string[100];

delete stringPtr1; // delete an objectdelete [] stringPtr2; // delete an array of // objects

Prefer using auto_ptr (smart pointers). That will avoid any leaks being caused by non-usage of delete.Arrays

An array is allocated with operator new using brackets. When using the delete operator for an array, empty brackets are required in the statement. If the bracket is not included , the destructor is not called for all elements of the array. If the brackets are not included in the delete when it is required, a memory leak will result.

For example:

//// Declare the pointer to a list of rectangle pointers.//rectangle **rectangle_ptr;

//// Call new for the array of rectangles. //rectangle_ptr = new rectangle *[ 100 ];for ( idx = 0; idx < 100; ++idx ) { rectangle_ptr[ idx ] = new rectangle(); }

//// The call to delete contains brackets.//delete[] rectangle_ptr;

//// This is a MEMORY LEAK - a dangerous situation!//delete rectangle_ptr;

Smart PointersSmart pointers are a smart way of avoiding leaks. Where ever possible, use auto_ptr (C++ standard library provided smart pointer).If you are not acquainted, then follow this link to smart pointer.Small implementation of auto_ptr (it can be greatly customized according to the needs).Disadvantage of smart pointer they cant be compared against NULL If(ptr == 0) orIf(ptr) or if(!ptr) //where ptr is auto_ptr Is not possible.

Casts or Type Conversions

Casts should be used sparingly since it is possible for some data to be lost in the conversion. It can be dangerous to make a cast from a data type that is smaller to a data type that is longer because of alignment issues. For example, converting a char pointer to an int pointer may not work when the char is on an odd byte boundary. Also, it can be dangerous to make a cast from a data type that is longer to a data type that is smaller because data can be lost.

Explicitly specify a cast when dealing with arithmetic expressions involving both signed data types and unsigned data types.

For example:

int x;unsigned int y;unsigned int z;y = ( unsigned int )x + z;

#define

Defines should be declared in the class header or source file.

Define names are suffixed with _d when the name is lowercase letters, or the define name can be all uppercase letters with no suffix.

Do not use the #define directive for numeric values since this hinders debugging code. Enumerated types are used for numeric values. The use of defines as a function is discouraged. If a define can be implemented in any other way (i.e., a function, an enumerated type, etc.), use the other way instead of creating a define.Prefer Const and Inline to define where ever possible.ENUM

Use enums for constants that are local to a class. Enum names can be prefixed with the name of the class to avoid collisions.

Enumerated types are declared in the class header or source file.

Whenever a numeric value is used in code statements, consider setting up an enum with that numeric value for use in the code statements. This allows easy modification of the numeric value in multiple locations when the enum value is changed. Also, the enum has the advantage of documenting the purpose of the number.

Note: Enumerated values must not be defined as a string of two or more characters.Function Pointers

Declaring a typedef for a pointer to a function is recommended for purposes of readability.

For example (note: header file and source file combined for simplicity):

class shape { public:

typedef int ( shape::*fn_shape_area_fncb )( void );

enum area_measure_type_e { square_inches = 1, square_feet = 2, square_yards = 3, square_miles = 4 };

int pi_; int radius_; int length_; int width_;

int area_circle( void ) { return pi_ * radius_ * radius_; }

int area_rectangle( void ) { return length_ * width_; }

int convert( int area, area_measure_type_e area_measure ) { // // Conversion would take place here. // return area; }

int size( fn_shape_area_fncb area_fncb_ptr, area_measure_type_e area_measure ) { int area; area = ( this->*area_fncb_ptr )(); return convert( area, area_measure ); }

void calc_size( void ) { // // Area_circle and area_square are function calls. // size( &fn_shape::area_circle, square_feet ); size( &fn_shape::area_rectangle, square_yards ); } };CollectionsCollections are used to hold many different objects, but the order that the iterator and collection are created and destroyed are important. The collection should be created before the iterator. When collections are to be cleared for use again later in the processing, the clr_and_destroy() function should be invoked. If the collection is no longer needed, delete all iterators first, then delete the collection without using the clr_and_destroy() function. The deletion of the collection in the destructor is more efficient than calling clr_and_destroy() then deleting the collection, which takes more instructions. The collection destructor will check for any objects to be deleted, therefore those steps will be duplicated in the clr_and_destroy() and the destructor of the collection.Data SourcesThe order of creation and deletion is important for all the classes used with data sources.

The order for creation of the data source classes should be in the following order:1. Data source.2. Selectors.3. Rows, cursors, inserters, deleters, and updaters. These can be in any order.

The order for deletion of the classes associated with data sources should remove the classes from the stack in the reverse order from how they were added. The order for the deletion of the data source classes should be in the following order:1. Rows, cursors, inserters, deleters, and updaters.2. Selectors.3. Data Source.

Structures

If a new structure is required, it is located in the class definition that requires it.

The allocated size of a structure may change from machine to machine dependent upon that machines alignment needs for specific data types.Global Data

The use of global data must be minimized. Principles of data encapsulation must be employed to minimize global data. It is preferable to have data that is private to an object as opposed to global data.

LocationImplementation classes belonging to a component are located on a specific component directory. The interface class for the component has the appropriate header on the include directory.General Transaction Data Elements (TDEs) and component specific TDEs are to be located on the general TDE subdirectories (the butde directory contains the header files and the butdes directory contains the implementation files).- Classes

A class definition consists of a header file and a source file.

Only one class should be defined per source file.

Within class declarations, the public, protected, and private data members and member functions will be declared in that order. If one of these is not required it is omitted. There should only be one section for each of these in the include file.

Classes must not define public data. This violates the principle of data encapsulation. Exceptions to this might include 'simple' classes that are not intended to hide their implementation.

The ways that a client can use a class is defined by the class interface. How the client interfaces into the class is important when considering which function to place in a class. A minimal yet complete class interface is an advantage since the resulting classes are more manageable and are easier to comprehend.

Components of a ClassHeader File

The header file declares the name of the class, the data members, and member functions for the class.

According to our standards, no code statements are placed in the header files. The one exception to this rule is inline functions and template classes which do not have source files. Inline functions should contain no more than one statement.Layout of the Header File

The following order is used for header files.

Proprietary Comment Box. History Section. #ifndef for preventing multiple inclusion. #define for preventing multiple inclusion. #includes in alphabetical order with #ifndef statements. #define for Component ID (Component headers only). Class forward declarations in alphabetical order. Class Description Comment Box. class keyword. Class name declaration. Class inheritance if applicable. public: keyword. Public - defines in alphabetical order. Public - enums in alphabetical order. Public - friends in alphabetical order. Public - structures. Public - default constructors with function comment boxes. Public - other constructors with function comment boxes. Public - copy constructors with function comment boxes. Public - destructors with function comment boxes. Public - overloaded operators in alphabetical order with function comment boxes. Public - overloaded friend operators in alphabetical order with function comment boxes. Public - member functions in alphabetical order with function comment boxes. protected: keyword. Protected - defines in alphabetical order. Protected - enums in alphabetical order. Protected - structures. Protected - default constructors with function comment boxes. Protected - other constructors with function comment boxes. Protected - copy constructors with function comment boxes. Protected - destructors with function comment boxes. Protected - overloaded operators in alphabetical order with function comment boxes. Protected - overloaded friend operators in alphabetical order with function comment boxes. Protected - member functions in alphabetical order with function comment boxes. Protected - data members in alphabetical order. private: keyword. Private - defines in alphabetical order. Private - enums in alphabetical order. Private - structures. Private - other constructors with function comment boxes. Private - copy constructors with function comment boxes. Private - overloaded operators in alphabetical order with function comment boxes. Private - member functions in alphabetical order with function comment boxes. Private - data members in alphabetical order. End of class declaration Code for inline functions End of #ifndef.

Declaration of Class Functions and Data

Obviously, all functions and data declared for the class are available for that class itself to use. However, there are restrictions for other classes based upon the area in which the function is declared in the class being used. Data hiding is one of the big advantages of C++. Public

All functions that are available to any other classes are declared as public. Data should not be public.Protected

Functions and data that are available to derived classes are declared here. If a class attempts to use a protected function or data member for a class that it does not inherit from, a compiler error results.

Protected data members are preferred over private data members where derived classes can use the data members directly without incurring any performance penalty and avoiding having to code all the get/set functions that would be required otherwise to deal with the data members. The get/set functions would match exactly the type of the data member, so if there is a change to the data member type, the get/set function would change as well. The destructor must be either public or protected. It can not be private or there can not be a derived class.

Private

Functions and data that are to be available only to the class itself are declared private. If a class attempts to use a private function or data member for another class, a compiler error results.

Always use the private keyword even though it is not necessary. This makes the level of access more visible and easier to discern.

Functions, constructors and operators that should not be publicly used should be defined private. This generates a compiler error if an attempt is made by a client to use a function, constructor or operator that is declared private.InheritancePublic Inheritance

When a class has an is-a relationship with another class, the relationship is translated to C++ code using public inheritance. Private Inheritance

Private inheritance should be used rarely. Scott Meyers refers to private inheritance as an "is implemented in terms of" relationship between two classes. This type of relationship is not the result of a design consideration for a class. It is only an advantage when coding a class because it can use the other class functions.

IncludesFormat of Include Statement

When declaring include files in a header file, use the format instead of "file.h".

Do not include directories in file name specifications. To better facilitate porting files across environments, directories must not be included in the file name specifications.

Do not put sections in the filename on include statements. Include statements should only contain the filename. The Tandem extension of appending only the section to be included is unacceptable as it is non-standard and non-portable.

//// This is unacceptable because of the section included, and because// of the missing #ifndef statement.//#include

//// This is acceptable.//#ifndef SHAPE_H#include #endifWhich files to Include

Only the include files that are required for the interface are put in the header file. If possible, class forward declarations are used instead of including the entire class header file.

The remainder of the include files are located in the source (.cpp) file. The files needed by the implementation are located in the source file. This helps hide the implementation and produces better encapsulation.Preventing Multiple Inclusion of Headers with #define

A single header file can only be included once in the course of a compile. Defines are used within every header file to prevent a header from being included multiple times. The use of this method is required in all header files.

The define name is based upon the class header file name. This define must be all uppercase letters.

Example for header file:

#ifndef SHAPE_H#define SHAPE_H

#endif // if not defined SHAPE_H

Example for source file:

#ifndef SHAPE_H#include #endif

For third party code (system libraries and STL included), the following example applies: #ifndef ALGO_H#include #ifndef ALGO_H#define ALGO_H#endif#endif

The define name in the third party header is ignored, and the engineer defines the include guard according to standards in the source that uses the #include. The standards for defining the third party include guard is to use the name of the include file in all uppercase letters. Replace the .h with _H. So, becomes ALGO_H for the define. If a file does not have a .h, still include an _H. For example, a header file named becomes CLOCALE_H.

Contents of the Include File

No code should go into the include file. Even if a function consists of a one line statement the code should be located in the source file. The exception to this is for inline functions and template classes.

Constructor Considerations

Classes must have a default constructor. If a constructor is not defined, the compiler automatically provides a default constructor. If a default constructor is not valid for a class, place it in the private section to prevent it from being used. This applies to the copy constructor and assignment operator, also.

If a class has an instance function (e.g. a Component Bridge), no constructors should be public.Initialization Order

List initialization data members in the order that they are declared. This avoids confusion over the order that members are initialized. According to the C++ standard, members are initialized in the order that they are declared, not the order that they appear in the initialization list. Data members should be declared in alphabetical order, so initialization lists should be in alphabetical order unless there is a reason for the data members not to be in alphabetical order.

Do not build constructors that have dependencies on the order of initialization of data members or static objects.

Use the initialization mechanism within constructors for base classes and all data members. This reduces the risk of an invalid state after successful construction.

For example:

bu_rectangle::bu_rectangle( const int lgth, const int width ) : fn_shape(), lgth_( lgth ), width_( width ) { }Indicating Status of Instantiated Objects

Obviously, object status values are not returned from constructors.

Instead, functions are provided for access to a given status. Note that it is possible that an operator could be overloaded to perform the same type of functionality, but that is not as intuitive as an explicit member function. The following three examples show correct and incorrect constructors.

This example shows the best way. No status value is returned in the constructor, and the member function used to determine if the object is valid is very intuitive.

For example:

//// This is correct//int width = 5;int lgth = 5;bu_rectangle square( lgth, width );

if ( square.area() < 0 ) { return false; }

//// Otherwise, the square object is valid, so continue processing.//...

This example is wrong. The constructor of the file object returns a value in the status variable. The client is required to define a status variable to hold the return status and then test the status.

For example:

//// This is wrong//int width = 5;int lgth = 5;int stat;bu_rectangle square( lgth, width, stat );

if ( !stat ) { return false; }

This example is wrong. Even though the constructor does not return a value, it is not very intuitive because the object has overloaded the "!" logical not operator.

For example:

//// This is wrong//int width = 5;int lgth = 5;bu_rectangle square( lgth, width );

if ( !square ) { return false; } Copy Constructors

All classes should contain a copy constructor. It is especially important for classes that contain pointer members. If copy semantics are allowed, the copy constructor must be made public. If they are not allowed the copy constructor must be made private. Private copy constructors must be defined but not necessarily implemented.

The default "copy constructor" generated by the compiler performs a bit-wise copy of member data. This is a shallow copy. For shallow copies, pointers are copied, but the data being pointed to is not copied. A deep copy is a copy where the data that the pointers are pointing to is copied as well. Be aware that shallow copies can cause data members for different objects to point to the same data. This could produce unexpected results when the data is deleted. The first object may delete the data, and the second object that still has a pointer to the memory is not aware of the memory being deallocated. If the second object attempts to access the data, the process will abend.

For example:

bu_rectangle::bu_rectangle( const bu_rectangle &rectangle ) { // // This is a deep copy of the coordinate_ptr_. A copy constructor for // the data member can be used. // coordinate_ptr_ = new bu_coordinate( rectangle.coordinate_ptr_ );

// // This is a deep copy of the figure_txt_ptr_. A new statement is executed // for the pointer followed by the copying of the value. // figure_title_ptr_ = new char[ ( sizeof( figure_title_def ) + 1 ) ]; strcpy( figure_title_ptr_, rectangle.figure_title_ptr_ );

}

Destructor Considerations

Destructors are declared as virtual for the sake of the derived classes. It is important that the destructors be called properly when the derived class is destroyed via a pointer to the parent class. When a destructor is declared as virtual, the derived class destructor causes the parent class destructor to be invoked.

Destructors should release all the resources owned by the object or leaks could result. It is not necessary to put logic in destructor for singletons, since the destructor should never get invoked.

Required Functions

The required functions for any class are default constructors, copy constructors, and assignment operator functions.Default Constructor

Classes must contain default constructors. If this function is not required, it can be placed in the private area to prevent its usage.Copy Constructor

Classes must contain copy constructors. If this function is not to be used by clients, it can be placed in the private area to prevent its usage.Assignment Operator Functions

The assignment operator function is similar to the copy constructor function. A shallow copy is dangerous for a class with pointers as data members. Assignment operator functions are defined so that deep copies are produced when the assignment operator is used.

Assignment operators must check for assignment to self. Assignment to self should not be executed as a memory leak could result.

Assignment operators must return a const reference to the assigning object which allows multiple assignments within a single statement.

For example:

//// Dimensions of a cube.//height = length = width;

Naming

Member function names should be descriptive of the service that it provides.

For example:

int bu_rectangle::area_get( void );

void fn_shape::fill( void );

void fn_shape::draw_outline( void );

All functions that have the same name should have similar behavior, varying only in the number and/or types of parameters.Inline Functions

Use of inline functions should be considered carefully because they cannot be debugged. However, the use of inline functions generally increases the performance of the code. Inline functions normally consist of one line of code. A more complicated function should not be made inline for the purposes of debugging. Examples of functions that may meet this criteria are mutator and accessor functions. Inline is a recommendation to the compiler, so there is no guarantee that the function will be made inline by the compiler. Do not use #define instead of inline functions.Templates

Template classes contain the function declarations and the function definitions in the header. Basic Elements of FunctionsReturn Type

When a function has a return type, the data returned must not be a pointer to a local variable. Once the function returns, the local variables within the function are deallocated when the function pops off of the stack. Function Arguments (parameters)

Data encapsulation implies that member functions must never return pointers or references to data that have less visibility than the function. So for example, a protected member function should not return to the client a pointer to a private data member.

Pass and return objects by reference instead of by value. Passing an object by value invokes the use of copy constructors, and it creates temporary objects.

Avoid functions with a large number of arguments so that function are less complicated. Try to keep the number of arguments for a function less than or equal to eight. Each parameter should be listed on a separate line in the function declaration.

Use the same parameter names in the declaration (header) and definition (code). This increases the clarity of the correspondence between declaration and definition.Temporary Objects

Minimize the number of temporary objects that are created. Temporary objects are often created when objects are returned from functions or when objects are passed by value to functions. The creation and deletion of temporary objects can adversely affect performance.Default Arguments

Use default arguments cautiously. The use of default arguments makes code less clear and increases the potential of creating problems when a function is overloaded. Use a default value for the last parameter only if it can be kept as the last parameter.Variable Arguments

Use overloaded functions instead of functions with a variable number of arguments. Variable argument lists defeats type checking and therefore are not advised. The use of ellipses is not recommended, but it can be used if a particular problem requires it.

It is acknowledged that some Tandem NSK calls require variable arguments.Const Arguments

Use constant references (const &) instead of call-by-value whenever possible when declaring non-scalar function arguments. This is more efficient when passing back non-scalar arguments since it avoids creating a copy of the argument on the stack. In the case of an object, creating a copy involves invoking the copy constructor and a destructor.Local VariablesDeclaration

In general, variables are declared immediately before the statement block in which the variable is used. The use of stack space for local variables is preferred over heap space.

For example:

//// This is wrong.//int x_ofst;int y_ofst;if ( point_ptr_ ) { for ( x_ofst = 0; x_ofst calc( y_ofst ); point_ptr_->coordinate_set( x_coodinate_ptr_, y_coordinate_ptr_ ); } } }

//// This is correct.//if ( point_ptr_ ) { int x_ofst; int y_ofst; for ( x_ofst = 0; x_ofst calc( y_ofst ); point_ptr_->coordinate_set( x_coodinate_ptr_, y_coordinate_ptr_ ); } } }

Do not declare the same variable name in different scope because of the ambiguities it causes in inspect.

For example:

//// This is wrong. Look at the variable called posn_ofst.//if ( point_ptr_ ) { if ( x_coordinate_ptr_ ) { int posn_ofst;

posn_ofst = point_ptr_->posn_get();

x_coordinate_ptr_->coordinate_set( posn_ofst );

if ( y_coordinate_ptr_ ) { int posn_ofst;

posn_ofst = point_ptr_->posn_get();

y_coordinate_ptr_->coordinate_set( posn_ofst );

} } }

For example:

//// This is correct.//if ( point_ptr_ ) { if ( x_coordinate_ptr_ ) { int posn_ofst_x;

posn_ofst_x = point_ptr_->posn_get();

x_coordinate_ptr_->coordinate_set( posn_ofst_x );

if ( y_coordinate_ptr_ ) { int posn_ofst_y;

posn_ofst_y = point_ptr_->posn_get();

y_coordinate_ptr_->coordinate_set( posn_ofst_y );

} } }

It is acceptable to limit the scope of variables in a member function. Declaring a variable with the smallest possible scope improves the readability of the code and results in performance improvements if the variable is not needed. If a variable is to be used no matter what occurs in a function, it should be declared at the beginning. If, however, its use is dependent on certain situations it can be declared only when needed.

Variables must each be declared in a separate declaration statement. This makes code more readable and can be useful to some editing tools.

For example:

//// This is wrong.//fn_coordinate *x_coordinate_ptr, *y_coordinate_ptr;

//// This is correct.//fn_coordinate *x_coordinate_ptr,fn_coordinate *y_coordinate_ptr;

Initialization

Initialize variables to default values only if required. It is only necessary to initialize variables to default values if a condition occurs where the variable can be interrogated without previously being set.Program StatementsExpressions

Use parentheses to make expressions evaluate in the correct order. Do not depend upon assumptions concerning evaluation order. Break Usage in loop-type statements

It is recommended to use break to exit a loop to avoid the use of flags.Switch statements

The default case within the switch statement must be used to prevent unpredictable behavior for the statement. Break statements are required at the end of a block of case statements when the engineer does not want the execution to continue processing the next set of case label statements.For statements

A for statement should use lower limits that are a valid value and upper limits that are not a valid value. The purpose of this rule is to avoid the case where the body is executed one too many or one too few times.

For example:

//// This is right.//for ( x_coordinate = lower_limit; x_coordinate < upper_limit; ++x_coordinate ) { }

If statements

If possible, use "return" in if-then-else statements to reduce nested levels.

If there is no code that needs to be executed after the execution of an if then else statement, return can be used to reduce the levels of nesting.

For example:

//// Poorly nested code.//if ( dimension == two_dimensional ) { z_coordinate_ptr_ = 0; return true; }elseif ( dimension == three_dimensional ) { z_coordinate_ptr_ = new fn_coordinate(); return true; }

return false;

The above block of code could be written as follows to reduce the levels of indentation and to make the code more readable, but can only be used when there is a return in the body of the if statement.

//// This is correct. //if ( dimension == two_dimensional ) { z_coordinate_ptr_ = 0; return true; }

if ( dimension == three_dimensional ) { z_coordinate_ptr_ = new fn_coordinate(); return true; }

return false;

When there is more than one simple statements inside the body of the if or else, a set of braces must be used to define the body of the statement.

if ( fnct_typ == encrypt ) { // // Check for the exact length required to be converted into ASCII // for AKB and non-AKB format. // if ( is_ver_284() ) { buf_lgth = 32; } else { buf_lgth = 90; } }

else { // // Function if Translate. Set the length to be converted into // ASCII accordingly. // if ( is_ver_284() ) { buf_lgth = 52; } else { buf_lgth = 168; } }

Another example of an if statement is one which can be used when a switch statement cant be implemented. The following type else if statements can only be used when there are no if statements in the body of the else if statement.

if ( dimension == two_dimensional ) { z_coordinate_ptr_ = 0; }else if ( dimension == three_dimensional ) { z_coordinate_ptr_ = 0; }else if ( dimension == one_dimensional ) { z_coordinate_ptr_ = 0; }

GOTO

Never use goto.

Assert Statements

Assert is a macro which detects coding errors. Only coding errors are to be detected with assert statements. For example, a read error that occurs on a file is not tested with an assert statement. Assert statements are compiled by defining the NDEBUG symbol. When the NDEBUG symbol is defined, the assert statements are replaced with an empty body of logic (essentially, no logic), so the statements are compiled out of the resulting executable.

The assert statement is passed an expression that must evaluate to the boolean value of true. When the assert expression fails, the assert macro logic invokes the appropriate exception. This may involve displaying an error message, aborting the process, whatever is appropriate.

The assert statements are linked to the documented preconditions and postconditions of each member function.

For example:

//// The precondition is that the rectangle pointer input argument// cannot be zero.//// The postcondition is that the rectangle must be drawn.//void shape::draw( const bu_rectangle * const rectangle_ptr ) { assert( rectangle_ptr != 0 );

construction_loc = rectangle_ptr->construct_it();

assert( construction_loc != 0 ); }

Assert statements are to be included in each member function that has a precondition and/or postcondition documented in the header file. The precondition assert statements are usually at the beginning of a function, but this is not always the case. It is appropriate to have an assert statement after some work has been done in the function. Deciding where to put the assert statement is different for each function, so it is up to the discretion of the engineer to pick the appropriate placement for the assert statement.

An assert statement should never do any work or change the behavior of the function. They can only perform tests and should not do any assignment type operations.

Function RecommendationsInheritance

Classes should be written so functions are declared as virtual for the purpose of inheritance.

Overload functions in the parent class only when the parent function is declared as virtual. Redefining a parent class function that is not a virtual function is dangerous and should be avoided. When the function of an object is invoked, a different function is called depending on whether the object pointer is defined as the parent class or the child class.

A derived class must not redefine the data type for a default parameter within an overloaded function. Doing so will produce unexpected results since the default values are resolved at compile time while the virtual function calls are resolved at run-time. OverloadingFunctions

Do not change the purpose of a function via overloading of the function. All versions of a function should have the same purpose.

Avoid overloading a function where the signature differs between a pointer and a numeric type. Incorrect results can occur when the client invokes the function using a numeric value.

For example:

//// This is wrong.//void fill_texture::repeat_interval_set( int a ) { }void fill_texture::repeat_interval_set( char *ptr ) { }

//// Confusion results here!//repeat_interval_set( 0 );

Operators

Operator overloading must be done in a manner that is consistent with the original intent of the operator. If the overloading of an operator is confusing or misleading, clients could easily use the operator incorrectly. Use operator overloading carefully.Exception Handling

For functions that may raise an exception, be prepared to handle any possible exceptions. This results in more robust code.

For functions that return error values, check the error code before proceeding. Code that does so is more robust.

Do not base logic upon assumptions regarding how the code is written within the invoked functions. Specific Types of FunctionsConst Functions

Declare member functions as const whenever possible to insure that data members are not modified by the client using the function. For example, accessor functions can be declared as const functions.Virtual Functions

Make all member functions virtual. Making all member functions virtual enables customization using inheritance.

Function that are not to be virtual are Constructors, Copy Constructors, Assignment Operators, Inline Functions and Static Functions.Accessor Functions (get functions)

Accessor member functions should have names of the form: _get(). These are functions which provide access to an object attribute. The word value is replaced with the name of the attribute being retrieved.

For example:int repeat_interval_get( void );

A Transaction Data Element (TDE) accessor function should return a boolean value.

For a TDE with multiple data fields which are set separately and the valid flag is set to true when some of these data fields are not present, a is_xxxx_set boolean function should be invoked before attempting to obtain data for the field that is not included in valid flag setting logic.

Mutator Functions (set functions)

Mutator member functions should have names of the form: _set(). These are functions which set an object attribute. The word value is replaced with the name of the attribute being altered.

For example:

void repeat_interval_set( int interval_value );

Friend

Minimize the use of friend. The use of friends violates the principles of encapsulation and makes classes more difficult to maintain.Non-Portable Classes

Do not use non-portable classes in the platform independent code. An example would be an ofstream class, which is not supported on the IBM CICS platform.New OperatorProduct classes should check the CSM Factory before creating class objects with the new operator. The key to the CSM Factory should equal the class name in lower case with quotes. For example:crd_impl_ptr_ = ( crd_impl * )fn_csm_factory::create( "crd_impl" );if ( !crd_impl_ptr_ ) { crd_impl_ptr_ = new crd_impl; }ConstructorProduct classes should use the default constructor (no parameters) to allow the class to be replaced using the CSM Factory. If the class provides an instance function, the constructor should be protected. This forces other classes to use the instance function.