Class Layout
Each class or structure definition should be preceded by a comment giving a brief description of the object, and its original author. The comment should conform to the following format :
/// \file Vector.h
/// \brief encapsulates a 4d Homogenous Point / Vector object
/// \author Jonathan Macey
/// \version 3.0
/// \date 28/9/09 Updated to NCCA Coding standard
/// Revision History :
/// Initial Version 24/11/04
/// \todo lots of tidying up and code optimizations
Member variables
All member variables should use the following prefixes :
m_ : Standard member
c_ : Constant
s_ : Static member
These make it clear that the variable is a member of class or struct, and give some information about its usage. For ease of debugging, the object variable in a singleton class should always be named s_this.
Variable and function declarations
The class should be declared in the following order, unless there are practical reasons why this cannot be done
Public member variables
Public member functions
Protected member variables
Private member variables
Protected member functions
Private member functions
Where possible, indent the names of class variables and class methods to make columns. The variable type or method return type is in the first column, and the variable name or method name is in the second column. The * of a pointer is in the first column because it improves readability when considered part of the type. This makes it easier to read the header as a list of functions and variables.
Never define multiple variables using comma declaration lists.
Example :
class TempClass
{
public:
TempClass();
~TempClass();
// make all classes protected to allow inheritance
// private should only be used if absolutly needed
protected:
int m_number;
float m_real;
ngl::Vector *m_device;
std::string m_text;
};
Virtual functions
With a complex class inheritance structure, it can be very difficult to understand how the methods of various classes interact with each other. One way to lighten the load of code maintenance is to be able to see if a function is virtual, and if so to which base class it belongs.
The following rules help to achieve this: - All virtual methods must carry the virtual keyword, even though the C++ standard does not require this. - The first declaration of a virtual method should carry comments that describe its purpose. - If the purpose is self-evident, the comment should indicate why it is necessary for the method to be virtual. - Where a virtual method is overridden, it should be commented to indicate which class the method is first declared in.
Example :
class BaseClass
{
public:
// try to interpret this input, return false otherwise
virtual bool HandleInput( Input const &_input );
};
class DerivedClass :
public BaseClass
{
public:
// from BaseClass
virtual bool HandleInput( Input const &_input );
};
Non-Virtual Functions
Avoid hiding a base-class function in a derived class. Declaring a non-virtual function with the same name as a base class function will hide all the base class functions with that name. Overloading only works in the same naming scope, you can’t add an overload in a derived class.
Operator Overloads
Operator overloading should only be used where it aids code readability, and the semantics are natural, obvious and intuitive. The semantics of the overload function should mirror the properties of the function when applied to fundamental types.
Casting operators should only be used where the target type is conceptualy the same as the source type. This can otherwise cause the code to implicitly cast parameters to functions which take different types, leading to ambiguity. So, its ok for string to auto-cast to a char*, but having Vector3 auto-cast to a Vector4 is bad.
Protection
Classes that cannot be copied safely should protect against this by declaring private ‘=’ operators, and private copy constructors. With C++ 11 we can use the keyword ```=delete``
For Example :
private:
SomeClass ( SomeClass const& ); // DO NOT IMPLEMENT
SomeClass& operator=( SomeClass const& ); // DO NOT IMPLEMENT
C++ 11
private:
SomeClass ( SomeClass const& )=delete; // DO NOT IMPLEMENT
SomeClass& operator=( SomeClass const& )=delete; // DO NOT IMPLEMENT
If you don’t want your class to have any virtual functions, explicitly declare your destructor non-virtual and make it private to prevent any inheritance from the class. This is only possible for classes that use the Create/Destruct or AddRef/Release patterns.
Function Names
Function names have the first letter of the name in lower case then each world capitalised after that, with no underscores between words.
For Example:
addEntry();
draw();
drawHull();
Where possible, functions/methods that return a bool result should have a name that looks like a question, eg
isValid();
hasProtocol();
areParallel();
Each function should be preceeded by a comment containing a brief description about the function (how it works, what it does) above the function body in the .cpp file.
Parameters
Each parameter variable is placed on its own line, indented with a 2-space tab. Avoid default arguments that require temporary construction of an unnamed temporary object, as this can cause significant performance issues. Function parameters are prefixed as follows to indicate their usage (input/output/input-output). The first character of the variable name itself is lower case, with the first letter of each subsequent word capitalised.
he following parameter prefixes are to be used :
_ Input Only (ie. _SampleID)
o_ Output Only (ie. o_Count)
io_ Input/Output (ie. io_SampleCount)
All input only parameters (prefixed with ‘_’) should also be marked as const so that the code can always assume that they contains the original values that were passed in unless standard types (int float etc)
For example :
void rotAxes(ngl::Vec3& io_a, ngl::Vec3& io_b, const Real _angle)
{
}
Complex types (structs or classes) should never be explicitly passed by value.
Class Constructors
Where a class constructor has an initialisation list, the ‘:’ is placed on the same line as the closing parameter bracket. Each entry in the list is then placed on a separate line, indented with a 1-space tab.
The initialisation list is split into 2 columns, with the variable names in one, and the initial values in the other.
For Example :/// \brief copy ctor
/// @param[in] _v the value to set
Vector(const Vector& _v) :
m_x(_v.m_x),
m_y(_v.m_y),
m_z(_v.m_z),
m_w(_v.m_w){;}
Local Variable names
The first letter of a local variable name is lower case, with the first letter of each subsequent word in upper case.
This is ok :float randomVariable
This is not :
float RandomVariable
Use of const
Parameters that are passed by value or reference should always be marked as const. For example :
void setNumber( const int _number)
{
// ...
}
This guarantees that they always retain the value that was passed in, reduces the ‘creeping death of const’ effect. It can also help the compiler optimizer to generate more efficient code in some cases.