Contents
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:
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;
void* psome_void_pointer;
const int USERS_NUMBER;
int i, j, n, m, tmp;
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;
PVOID pSomeVoidPointer;
const INT USERS_NUMBER;
int i, j, n, m, tmp;
namespace MyNameSpace;
vector<int> nUsers;
vector<char *> pszUserNames;
class CSomeClass;
INT DoWork(INT nTimeOfDay);
FLOAT CalculateRadius(const& Circle rCircle);
INT StartIOManager();
INT OpenDvdPlayer();
.NET platform (Hungarian notation, and prefixes are obsolete):
int someVariable;
float barWeight;
unsigned int usersNumber;
bool isEngineStarted;
double circleArea;
double circleArea;
void^ someVoidPointer;
const int UsersNumber;
int i, j, n, m, tmp;
namespace MyNameSpace;
array<int> users;
array<String> userNames;
class SomeClass;
int DoWork(int timeOfDay);
float CalculateRadius(const& Circle circle);
int StartIOManager();
int OpenDvdPlayer();
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.
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);
}
}
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);
}
}
For function and class definitions, it is unambiguous if you put the opening brace on the next line:
void some_function(int param)
{
}
class SomeClass
{
};
But for ifs
, for
s, while
s 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); e =f-( (g&h ) >>10);
Wrap your header file contents with the multiple inclusion guard to prevent double inclusion:
#ifndef Foo_h
#define Foo_h
#endif
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++.
class SomeClass
{
};
class SomeClass
{
public:
SomeClass() { } ~SomeClass() { } SomeClass(const SomeClass &rhs); SomeClass& operator=(const SomeClass& rhs); SomeClass* operator&(); const SomeClass* operator&() const; };
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();
const ClassName& operator=(const ClassName& classname);
int do_work();
int start_engine();
int fire_nuke();
inline unsigned int get_users_count() const;
inline bool is_engine_started() const;
protected:
private:
unsigned int m_users_count;
bool m_is_engine_started;
};
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 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++;
int a;
int & ra1 = &a;
const int& ra2 = &a;
a = 10;
ra1 = 15; ra2 = 20; int copy(char* pdest, const char* psour)
{
}
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; int i = pobj1->get(); pobj1->set(0, 10);
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 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; pfoo->set(0, 10);
pdata = data2; pfoo = &foo2;
You can also declare a const object and a pointer simultaneously:
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);
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(); bool is_engine_started();
Place &
or *
with a type, not with a variable. This makes it more obvious what the type is.
int& rval; float* pdata;
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;
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); void set_point(int x, int y);
Comparing a pointer against zero:
void some_function(char* pname)
{
if (!pname) {
}
if (pname == NULL) {
}
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.
BigObject modify_it(BigObject big_object)
{
...
return big_object;
}
void modify_it(BigObject& big_object)
{
}
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; }
const SomeObject& get_object() { return m_object; }
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(); ...
};
class Derived : Base
{
...
};
...
Derived* pderived = new Derived();
Base* pbase = (Base *)pderived;
...
delete pbase; ...
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; phelper_class->m_variable = 10;
...
}
class HelperClass
{
friend class MainClass;
...
private:
int m_variable;
...
};