Click here to Skip to main content
Click here to Skip to main content

C++ Coding Practices Guide

By , 20 May 2008
 

Contents

Introduction

To write consistent code comprehended by other developers, you are supposed to follow some coding styles and practices, rather than inventing your own ones. These include naming conventions (on how you name your variables and functions), code and class layout (including tabs, whitespace, brackets placement), imperative const correctness etc... To follow this article, you are supposed to know the basics of OOP and C++. There are some great online resources you may use in addition to this article:

Naming Convention

The first task you encounter when you start to write code is how to name your variables, objects, and functions. The importance of naming conventions can not be underestimated. Providing proper naming will result in self-documenting code, easily comprehended by others. Unless you want to obfuscate the source code, you should follow these guidelines.

Use a name that unambiguously describes a variable or object. A function should contain in its name a verb of the action that it implements.

Linux style (use underscores to separate words, names are lowercase):

int some_variable;
float bar_weight;
unsigned int users_number;
bool is_engine_started;
double circle_area;
double m_circle_area; //class private variable, prepend m_ prefix

void* psome_void_pointer;

const int USERS_NUMBER;

int i, j, n, m, tmp; //some local loops variables, temporary variables

namespace mynamespace;

vector<int> users;
vector<char *> user_names;

class SomeClass;

int do_work(int time_of_day);
float calculate_radius(const Circle& rcircle);
int start_io_manager();
int open_dvd_player();

Windows style (MFC applications, prepend Hungarian prefixes to identify the variable type):

INT nSomeVariable;
FLOAT fBarWeight;
DWORD dwUsersNumber;
BOOL bIsEngineStarted;
DOUBLE dCircleArea;
DOUBLE m_dCircleArea;  //class private variable, prepend m_ prefix

PVOID pSomeVoidPointer;

const INT USERS_NUMBER;

int i, j, n, m, tmp;  //some local loops variables, temporary variables

namespace MyNameSpace;

vector<int> nUsers;
vector<char *> pszUserNames;

class CSomeClass;

INT DoWork(INT nTimeOfDay);
FLOAT CalculateRadius(const& Circle rCircle);
INT StartIOManager();
INT OpenDvdPlayer(); //if abbreviation takes more than
                     // 2 letters do not capitalize the whole word

.NET platform (Hungarian notation, and prefixes are obsolete):

int someVariable;
float barWeight;
unsigned int usersNumber;
bool isEngineStarted;
double circleArea;
double circleArea; //refer to a class private variable
                   // in code as this->circleArea

void^ someVoidPointer;

const int UsersNumber;

int i, j, n, m, tmp;  //some local loops variables, temporary variables

namespace MyNameSpace;

array<int> users;
array<String> userNames;

class SomeClass;

int DoWork(int timeOfDay);
float CalculateRadius(const& Circle circle);
int StartIOManager();
int OpenDvdPlayer();  //if abbreviation takes more than 2 letters
                      // do not capitalize the whole word

Tabs

Tabs are 8 spaces, so the indentations are also 8 characters. The whole idea is to clearly define where a block of control starts and ends, and you'll find it a lot easier to see how the indentation works if you have large indentations. It also has the added benefit of warning you when you're nesting your functions too deep.

//very bad
void FaceDetector::estimate_motion_percent(const vec2Dc* search_mask)
{
  if (search_mask == 0) { m_motion_amount=-1.0f; }
  else 
    {
     unsigned int motion_pixels=0;
     unsigned int total_pixels=0;
      for (unsigned int y = get_dy(); y < search_mask->height()-get_dy(); y++) 
      {
       for (unsigned int x=get_dx(); x < search_mask->width()-get_dx(); x++) 
       {
           total_pixels++;
           if ((*search_mask)(y,x)==1) motion_pixels++;
       }
      }
    m_motion_amount = float(motion_pixels)/float(total_pixels);
   }
}

//very good
void FaceDetector::estimate_motion_percent(const vec2Dc* search_mask)
{
        if (search_mask == 0)
                m_motion_amount = -1.0f;
        else {
                unsigned int motion_pixels = 0;
                unsigned int total_pixels = 0;
                for (unsigned int y = get_dy(); y < 
                           search_mask->height() - get_dy(); y++) {
                        for (unsigned int x = get_dx(); x < 
                                 search_mask->width() - get_dx(); x++) {
                                total_pixels++;
                                if ((*search_mask)(y, x) == 1)
                                        motion_pixels++;
                        }
                }
                m_motion_amount = float(motion_pixels) / float(total_pixels);
        }
}

Spaces, Braces, and Parenthesis

For function and class definitions, it is unambiguous if you put the opening brace on the next line:

void some_function(int param)
{
        //function body
}

class SomeClass
{
        //class body
};

But for ifs, fors, whiles etc..., there are several possibilities for the braces and parenthesis:

if(some_condition)
{
        //...
}

if( some_condition )
{
        //...
}

if ( some_condition )
{
        //...
}

if (some_condition)
{
        //...
}

if (some_condition) {
        //...
}

The preferred way, which is the last, and proposed by K&R, is to put the opening brace last on the line, and put the closing brace first. The reason is to minimize the number of empty lines.

The spaces in math expressions should follow this style:

float a, b, c, d;
int e, f, g, h;

a = (b + d) / (c * d);

e = f - ((g & h) >> 10);    //good
e =f-( (g&h ) >>10);        //bad

Multiple Inclusion Guard

Wrap your header file contents with the multiple inclusion guard to prevent double inclusion:

#ifndef Foo_h
#define Foo_h

// ... Foo.h file contents

#endif

Class Layout

When you declare a class, remember that if you do not provide definitions for:

  • constructor
  • copy constructor
  • assignment operator
  • address-of operator (const and non-const)
  • destructor

they will be provided automatically by C++.

//declaring that class
class SomeClass
{
};

//you get these functions provided automatically
class SomeClass
{
public:
        SomeClass() { }                     //constructor
        ~SomeClass() { }                    //destructor
        SomeClass(const SomeClass &rhs);    //copy constructor
        SomeClass& operator=(const SomeClass& rhs); //assignment operator
        SomeClass* operator&();             //address-of
        const SomeClass* operator&() const; //operators;
};

If you do not intend to provide a copy constructor and an assignment operator, and your class allocates some memory in the constructor and frees it in destructor, declare a copy constructor and an assignment operator as private members to avoid accidental class object cloning and deleting the same memory twice, in the original object and in its copies.

Provide class declarations in the public, protected, and private order. This way, the users of a class will be able to immediately see the public interface they need to use:

#ifndef ClassName_h
#define ClassName_h

class ClassName
{
public:
    ClassName();
    ClassName(const ClassName& classname);
    virtual ~ClassName();

// Operators
    const ClassName& operator=(const ClassName& classname);

// Operations
    int do_work();
    int start_engine();
    int fire_nuke();        

// Access
    inline unsigned int get_users_count() const;        

// Inquiry                
    inline bool is_engine_started() const;

protected:
    //protected functions for descendant classes
        
private:
    //private data and functions
    
    unsigned int m_users_count;
    bool m_is_engine_started;

};

// Inlines
inline unsigned int SomeClass::get_users_count() const
{
    return m_users_count;
}

inline bool SomeClass::is_engine_started() const
{
    return m_is_engine_started;
}

#endif ClassName_h

Keep inlines in header files and outside the class definition, do not garbage the class body.

Const Correctness

Const correctness is to use the keyword const to explicitly declare and prevent the modification of data or objects which should not be modified. Use it at the very outset, as fixing const correctness later will be a nightmare.

You can provide it for data or class member function declarations:

  • const declaration;
  • member-function const;
const int max_array_size = 1024;
max_array_size++;    //compilation error

int a;
int & ra1 = &a;
const int& ra2 = &a;
a = 10;
ra1 = 15;    //ok
ra2 = 20;    //compilation error
//the char data pointed by psour will not be modified
int copy(char* pdest, const char* psour)
{
   //copy data from psour to pdest
}

Declaring a member function with the const keyword specifies that the function is a "read-only" function that does not modify the object for which it is called.

class Foo
{
    //...
    void set(int x, int val);
    int get(int x) const;
    //...
};

const Foo* pobj1;        //modification of the pobj1 is forbidden
int i = pobj1->get();    //you can only call const member-functions
                         //with const pointer to class object
pobj1->set(0, 10);       //compilation error

However, declaring a class member variable as mutable allows it to be modified by a const function.

class SomeClass
{
public:
    void some_function() const;
private:
    mutable int a;
};

void SomeClass::some_function() const
{
    a++;    //you can modify a in const function
}

You can prevent a pointer once assigned to be reassigned to point to another place. This is similar in behaviour to a reference declaration.

Foo foo1, foo2;
int data1[100];
int data2[100];

Foo* const pfoo;
int* const pdata;

pdata = data1;
pfoo = &foo1;

*pdata = 100;        //you can change the data
pfoo->set(0, 10);    //or class object

pdata = data2;       //compilation error
pfoo = &foo2;        //you can not reassign the pointer

You can also declare a const object and a pointer simultaneously:

//neither Foo object can be changed 
//nor pfoo changed to point to another Foo object
const Foo* const pfoo;
const int* const pdata;

Finally, if you provide () or [] operators, provide their const versions also to be used by "read-only" objects.

class SomeClass
{
    ...
    inline int& operator()(int x);      
    inline int operator()(int x) const; 
    inline int& operator[](int x);      
    inline int operator[](int x) const; 
    ...
};

Now, you will be able to use ordinary and const versions of the class object:

SomeClass a;
const SomeClass b;

int i = a(0);
int j = b(0);
a(1) = b(2);
b(3) = a(0);    //compilation error, b object can not be modified

Function Return Value

Functions return values of different types. One of the most typical is a value indicating whether the function succeeded or failed. Such a value can be represented as an error-code integer (negative = failure, 0 = success) or a "succeeded" boolean (0 = failure, non-zero = success).

If the name of a function is an action or an imperative command, the function should return an error-code integer. If the name is a predicate, the function should return a "succeeded" boolean.

int start_engine();           //0 = success, negative = error-codes
bool is_engine_started();     //true = success, false = error

Miscellaneous

Place & or * with a type, not with a variable. This makes it more obvious what the type is.

//good
int& rval;       //&#39;it is&#39; reference int
float* pdata;    //&#39;it is&#39; pointer float

//bad
int &rval;
float *pdata;

Define variables that can not have negative values as unsigned (e.g., unsigned int array_length rather than int array_length). This will help to avoid inadvertent assignment to a negative value.

Reference is a const pointer like int* const p, which once assigned, you can not reassign.

int var1 = 100;
int var2 = 200;

int& ref = &var1;
ref = &var2;        //avoid it!

To provide a constant double pointer for Foo** ppfoo, use the declaration const Foo* const* ppfoo.

Always provide formal arguments in function definitions:

void set_point(int, int);       //ambiguous, what are parameters? avoid it, 
void set_point(int x, int y);   //good.

Comparing a pointer against zero:

void some_function(char* pname)
{
    //bad 
    if (!pname) {
            //...
    }
    
    //error sometimes
    if (pname == NULL) {
            //...
    }
    
    //good
    if (pname == 0) {
            //...
    }
}

Pass by reference or pointer, class objects in function parameters. If you pass by value, the whole copy of the class object will be created, which will be bad if it occupies some megabytes of storage.

//avoid it, pass big_object by reference or pointer
BigObject modify_it(BigObject big_object)
{
    //a local copy of big_object will be created
    ...
    return big_object;
}

//good practice
void modify_it(BigObject& big_object)
{
    //no copy is created, you can modify the object as usually
}

OOP Tips

Never return a pointer or non-const reference to private data members of a class. Do not break the encapsulation principle.

class SomeClass
{
public:
    SomeObject* get_object() { return &m_object; }
    //do not break incapsulation principle
    //m_object can be modified outside the class

    const SomeObject& get_object() { return m_object; }
    //good, the m_object will not be
    //inadvertantly modified outside the class        
private:
    SomeObject* m_object;
};

Always declare a virtual destructor in a public class. If you derive some class from its parent without a virtual destructor, then if you cast back the derived class object pointer to the parent class and later delete the parent class pointer, only parent destructor will be invoked:

class Base
{
    ...
    ~Base();    //should be virtual ~Base()
    ... 
};

class Derived : Base
{
     ...
};

...
Derived* pderived = new Derived();
Base* pbase = (Base *)pderived;
...

delete pbase;    //only ~Base() will be invoked
...

Use friends. If you do not intend some class to be used by the users of your library, but you access private members of that class from other public classes, providing public accessor methods for it is superfluous. Declare your class as a friend of it, and now you will be able to access its private data.

class MainClass
{
    ...
    void some_function();
    ...
    HelperClass* phelper_class;
    ...
};

void some_function()
{
    ...
    int i = phelper_class->m_variable;    //you gain access to private members 
                                          //of HelperClass from MainClass
    phelper_class->m_variable = 10;
    ...
}

class HelperClass
{
    friend class MainClass;
    ...
private:
    int m_variable;
    ...
};

License

This article, along with any associated source code and files, is licensed under The GNU General Public License (GPLv3)

About the Author

Chesnokov Yuriy
Engineer
Russian Federation Russian Federation
Member
Former Cambridge University postdoc (http://www-ucc-old.ch.cam.ac.uk/research/yc274-research.html), Department of Chemistry, Unilever Centre for Molecular Informatics, where I worked on the problem of complexity analysis of cardiac data.
 
As a subsidiary result we achieved 1st place in the annual PhysioNet/Computers in Cardiology Challenge 2006: QT Interval Measurement (http://physionet.org/challenge/2006/)
 
My research intrests are: digital signal processing in medicine, image and video processing, pattern recognition, AI, computer vision.
 
My recent publications are:
 
Complexity and spectral analysis of the heart rate variability dynamics for distant prediction of paroxysmal atrial fibrillation with artificial intelligence methods. Artificial Intelligence in Medicine. 2008. V43/2. PP. 151-165 (http://dx.doi.org/10.1016/j.artmed.2008.03.009)
 
Face Detection C++ Library with Skin and Motion Analysis. Biometrics AIA 2007 TTS. 22 November 2007, Moscow, Russia. (http://www.dancom.ru/rus/AIA/2007TTS/ProgramAIA2007TTS.html)
 
Screening Patients with Paroxysmal Atrial Fibrillation (PAF) from Non-PAF Heart Rhythm Using HRV Data Analysis. Computers in Cardiology 2007. V. 34. PP. 459–463 (http://www.cinc.org/archives/2007/pdf/0459.pdf)
 
Distant Prediction of Paroxysmal Atrial Fibrillation Using HRV Data Analysis. Computers in Cardiology 2007. V. 34. PP. 455-459 (http://www.cinc.org/archives/2007/pdf/0455.pdf)
 
Individually Adaptable Automatic QT Detector. Computers in Cardiology 2006. V. 33. PP. 337-341 http://www.cinc.org/archives/2006/pdf/0337.pdf)

Sign Up to vote   Poor Excellent
Add a reason or comment to your vote: x
Votes of 3 or less require a comment

Comments and Discussions

 
You must Sign In to use this message board.
Search this forum  
    Spacing  Noise  Layout  Per page   
GeneralMy vote of 1memberAescleal23 May '10 - 10:16 
Mostly trivia - misses out on several important C idioms, e.g. No RAII, no exception handling, no swapping assignment operators.
GeneralMy vote of 2memberJoe Woodbury28 Jun '09 - 18:12 
Found that many items in this are wrong or simply ridiculous.
 
For example, saying that checking a pointer with a not (i.e. if (!pInt)) is bad is simply not true. It is identical to comparing it to a zero. Comparing to a NULL is arguably syntactically cleaner and to my knowledge is defined as 0 by most compilers.
 
Like many articles, this completely misunderstand Hungarian notation; it isn't supposed to describe the type, but the usage. (Then again, everyone gets it wrong.)
 
Concerning braces: "The preferred way, which is the last, and proposed by K&R..." K&R proposed no such thing. The only reason that style was used in their book was to save paper. (I find the the first and fifth example to be the most common.)
 
And tabs of 8? I haven't seen that since forever. I've had team leads demand 2, 3 and 4, but never 8.
 
Your discussion of const was good and something oft misunderstood, so I gave you a 2.
QuestionRubik face color detect.membermerovingi08 Apr '09 - 22:41 
Hi! Can you help me?
 
I want detect rubik colors face and write the color detect value usign hue
in http://www.kociemba.org/cube464.zip in program exist what you want.
I have create a program and use webcam correctly.
and how I can draw little rects (like camera hotspot] one for each color face?
 
Thanks!
GeneralEmpty lines in source codememberMa3ztro8 Oct '08 - 4:54 
"The preferred way, which is the last, and proposed by K and R, is to put the opening brace last on the line, and put the closing brace first. The reason is to minimize the number of empty lines."
 
This reason is pretty subjective to me. Who said that there was a need to minimize empty lines in a source code? How does empty lines affects coding efficiency? ...Personally, I prefer the MFC bracket opening and closing BECAUSE of the empty lines that makes the code less compact and easier to read. At best the justification could be "for personal preferences", but I consider more important to have code easy to read and understand than a compact source code.
GeneralconstmemberMember 165205130 May '08 - 5:43 
I like the way you think. In the past I tried to implement some coding standards in the company I worked for but that resulted in a lot of discussions but no standards.
 
One item I remembered from a session with one of the members of the C++ standards committee was about the 'const' keyword. He said that the 'const' keyword must never be in front of the datatype.
 
const int i; // Avoid
 
Reason for this is that in some cases you do not know what is actually the case.
 
const int* i; // Is this a const ptr to an int or a ptr to a const int
 
Maybe it is clear when you have some experience but in the following case
 
typedef int* intptr_typedef;
#define int* intptr_define
 
const intptr_typedef i1; // const pointer to an int
const intptr_define i2; // pointer to a const int
 
Better is to put it after the type and read what you have written from right to left:
 
int const* i; // 'i' is a '*' to a 'const' 'int'
 
intptr_typedef const i1; // 'i1' is a 'const' 'intptr_typedef'
intptr_define const i2; // 'i2' is a 'const' 'intptr_define'
 
This reason worked for me.
GeneralWhere to put bulk of #includes, in .h or .cpp :PmemberFocusedWolf29 May '08 - 14:18 
This is funny i think... when i had c++ classes in college and highschool my style was to use this style:
 
// myHeader.h file
#ifndef BLAH_H
#define BLAH_H
 
#include <blah>;
#include <blah>;
#include <blah>;
 
//...many includes that my class needs for any types used both in .cpp and .h files
 
class Blah {};
 
#endif
 
///////////////////////
 
// .cpp file
 
#include "myHeader.h" //ONLY 1 Include!
 
//define functions here
</blah></blah></blah>
 
However recently i refreshed my c++ knowledge cause i needed it for something... so while breezing through the book i saw this style
 
// myHeader.h file
#ifndef BLAH_H
#define BLAH_H
 
#include <blah>;
#include <blah>;
#include <blah>;
 
//...many includes, BUT ONLY WHAT THIS FILE NEEDS TO COMPILE!
class Blah {};
 
#endif
 
///////////////////////
 
// .cpp file
 
#include "myHeader.h"
#include <stuff>
#include <muchmorestuff>
 
//...many includes, but specifically the main header file and for whatever types were needed within the { } of the functions written here EVEN IF THEY WERE INCLUDED IN THE HEADER FILE AS WELL!
 
//define functions here
</muchmorestuff></stuff>
 
Personally i still think the first method is best because i can maintain all my headers in one location... but then again they sorta pollute the header file... so really which do you think is best? :P
GeneralRe: Where to put bulk of #includes, in .h or .cpp :PmemberArman Z. Sahakyan30 May '08 - 2:55 
The second one! As less includes are in a header file as better. 'Better' means your code will have less dependencies between headers which means making an even little change in a header file, your copmiler will not parse all the headers where the changed header is included (directly or indirectly). This is a very important technique for large-scale projects (and perheps mid-scale ones too).
 
--
=====
Arman

GeneralWidth and Height of functionsmemberArman Z. Sahakyan29 May '08 - 3:59 
For large-scale projects, the coding standard may also normalize number of lines and line length (max character count in a line) of functions. For a very large project, I'm aware of, the standard defines 25 lines of max height and 80 characters of max width. Those numbers are not magic Smile | :) but the trick works.
 
--
=====
Arman

AnswerMiscellaneousmemberJKuhn27 May '08 - 5:29 
Usage of & and *: Your recommendation to use
int* i;
instead of
int *i;
works fine for the declaration of one variable or function parameters. For
int* i, j;
it doesn't because it results in
int* i; int j;
Therefor I prefer
int *i, *j;
because it is clear.
GeneralRe: MiscellaneousmemberStefan6327 May '08 - 6:10 
I agree that you run into problems on declarations with multiple variables. This is the reason I usually don't do this at all (i. e. declaring multiple variables in one statement). There's also another reason: it's a good practice to always initialize a variable the moment you declare it, and you can do this only when you do one variable per declaration! (ok, I've never bothered to find out whether you can actually do multiple initializations in one declaration, but I wouldn't care to do that anyway)
 
Once you restrict yourself to one variable per declaration, moving the '*' or '&' towards the type becomes natural: the type in your example is 'pointer to int', not 'int, maybe...'. Wink | ;)
 
What I'd do is this:
int* i(NULL); // variable of type int*, initialized with 0
int* j(NULL); // dito
int k(0); //variable of type int, initialized with 0
 
not this:
int *i, *j, k; // variables of type int ... err no, int* on second glance ... or at least some are  :^) 
i = NULL; // uhoh, better not forget to initialize!
k = 0; // also initialize, hope I've got them all...

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Permalink | Advertise | Privacy | Mobile
Web04 | 2.6.130523.1 | Last Updated 20 May 2008
Article Copyright 2008 by Chesnokov Yuriy
Everything else Copyright © CodeProject, 1999-2013
Terms of Use
Layout: fixed | fluid