-
Notifications
You must be signed in to change notification settings - Fork 1.5k
Coding style guide (C plus plus)
- Naming conventions
- Style conventions
- Const and exception correctness
- Memory management
- Containers use
- Exceptions and errors
- Performance
- Use descriptive/representative variable/class names.
- Use
g_
for global or namespace variables, e.g.g_outputFile
. - Use
gs_
for static at global or namespace scopes, e.g.gs_alertsContainter
. - Use
s_
for static variables local to a function, e.g.s_internalMap
- Use PascalCase for class names, as well as any aliases defined by typedef or using.
- Exception: For classes inherited from
std::exception
it is preferable to use the standard convention, e.g.system_error
,runtime_error
,dbsync_error
.
- Exception: For classes inherited from
- Use PascalCase for Enum values, e.g.
enum ErrorCodes { Failure = 0, Success = 1, ...}
. Avoid the use of negative numbers for enumeration values. - Use camelCase for function names.
- Use camelCase for variable names and parameter names.
- Constants must use ALL_CAPS_WITH_UNDERSCORES (defines or constexpr).
- Use lower-case p prefix to indicate a pointer, e.g.
pMyObject
. - Use lower-case sp prefix to indicate a smart pointer, e.g.
spYourObject
. - Use
m_
for instance member variables, e.g.m_isConnected
.- Exception: For POD (Plain Old Data) objects it is allowed to use member variables without
m_
. This represents objects (structs) with constant values like data holders.
- Exception: For POD (Plain Old Data) objects it is allowed to use member variables without
- Use
ms_
for static member variables, e.g.ms_managerInstance
.
- Avoid repeating class names in functions which perform some action on this as a whole. For example:
- The Employee class function to persist the entire object would be
save()
rather thansaveEmployee()
- But the function to delete the employee's medical data would be
deleteMedicalData()
because it doesn't act upon this as a whole.
- The Employee class function to persist the entire object would be
- Avoid using the “get” prefix for methods to obtain class attributes.
- Use lower case for new folders being created.
- Use camelCase for new files being created.
- Use descriptive related prefix for new files, e.g.:
osQueryManager.h
- Ensure your development environment is set to insert soft tabs at 4 spaces per tab.
- Braces always start on a new line apart from an empty function body which should appear on the same line, e.g.
virtual void finish() {}
. - Always use braces after if, for, while, etc. even if only one statement in block.
- Use curly braces for initialization, e.g.
int result{ EXIT_SUCCESS };
rather thanint result = EXIT_SUCCESS;
. - Use empty curly braces to zero-initialize C-style structs and simple data types, e.g.
size_t bufferLength{};
rather thansize_t bufferLength{ 0 };
. - Use using instead of typedef.
- Lines must not be longer than 120 characters, so break out function declarations over multiple lines.
- Use alignment of parameter names, field names, assignment operators, etc. to improve legibility.
- In header files, preferred use of:
#ifndef _HEADER_FILENAME_H
#define _HEADER_FILENAME_H
...
#endif
Instead of #pragma once
. This is not C++ standard.
- For boolean expressions avoid comparing anything with true or false , e.g.
if (isConnected == true)
should be written as if(isConnected)
. - For infinite loop preferred using
for (;;) {...}
rather thanwhile (true) {...}
.
- Use
const
for any object whose value does not change.- Exception: An object which is to be returned must not be const because this prevents use of move constructor.
- Use
const
for parameters passed by value. - Use
const
for any instance member function which doesn’t modify this. - Use
const
for objects of a simple type (integer, floating point, boolean, etc.), unless the callee is expected to modify the value. - Use
const T&
for objects of a complex type T, unless the callee is expected to modify the object. - Use
const T* const
for objects of a complex typeT
if a null reference is permitted. -
std::shared_ptr
andstd::unique_ptr
should never be passed by value. - Use
noexcept
for any function which cannot throw an exception. - Use final for any class which cannot be sub-classed.
The lifetime of all heap objects must be managed by a smart pointer, even if that lifetime is just a couple of a lines of code:
- Avoid using raw C pointers, instead use smart pointers as desired (
std::unique_ptr
,std::shared_ptr
). - For normal heap objects use the
std::unique_ptr
orstd::shared_ptr
templates. The latter must only be used when ownership of the object is shared. - Use smart deleters along with the smart pointers to avoid memory leaks and bad frees/structure closures:
struct smartDeleter
{
void operator()(<T> data)
{
// data free
}
};
const std::unique_ptr<T, smartDeleter> spSomething{ };
- Preferred use of
std::vector<>
STL structure, all vector elements are placed in contiguous storage and are faster than other data types structures. - Use
std::set<>
when it is needed to hold non-repeated data.
-
Use
auto
where possible instead of explicit types. -
Use
std::variant
instead of a C-style union. -
Use
std::optional
for variables and members which can legitimately be in an uninitialized state. -
Favour C++ Standard Library over Boost equivalents.
-
Use C++
nullptr
instead ofNULL
. -
Use C++ casts (
static_cast<>
,reinterpret_cast<>
, orconst_cast<>
as appropriate) instead of C-style casts. -
If a new object is to be immediately owned by a smart pointer, construct using
std::make_shared
orstd::make_unique
instead of new . -
Avoid using magic numbers like 20, 500, 1000. Instead, define constants with meaningful names.
-
Maintain one exit point apart from where exceptions are thrown.
-
Member variables appearing in a constructor's initializer list must be in the same order in which the members are defined.
-
The return value of non-void system/library functions calls should be checked.
-
Define variables as late as possible and in the narrowest possible scope (without affecting performance).
-
In derived classes, implementation of interface methods should be private or protected.
-
Functions should be layered and have single responsibilities.
-
A class function which does not require this must be declared static (and consider if it might be more appropriate to just have it as a non-class static function in the .cpp file).
-
If a variable value will be always > to 0 used unsigned int type.
-
For string values which need to represent expressions, statements, etc it is preferred the use of raw strings value for clarity and to avoid scape endings issues e.g:
const auto insertSql{ "{\"table\":\"processes\" ... "}]}"};
Should be replaced by:
const auto insertSql{ R"{"table":"processes" ... "}]}"};
-
Use emplace_back instead of push_back whenever possible.
- Throw exceptions for any failure that is not ignorable.
- Functions must only use a result code to denote ignorable failures, e.g. a hypothetical
deleteFile()
function might return false to indicate that a file doesn't exist but would throw an exception if deletion fails for some other reason. - In some cases, it is useful to let the caller choose if an expected failure should throw or return false, e.g. the aforementioned
deleteFile()
function might have an optional final parameter which determines if an exception should be thrown if the file doesn't exist. - Make Win32 API calls through a wrapper which throws std::system_error on failure.
- Avoid unnecessary copying of strings. This most frequently occurs when string is formatted in a local
char[]
orwchar_t[]
buffer and then astd::string
orstd::wstring
is constructed for return to the caller. - Reserve the size of
std::vector
if you know it in advance. - When using for-each to iterate over a container, declare the element type as
auto&
orconst auto&
as appropriate. - In scenarios where a container must be locked to ensure safe read access but will not be modified, use
std::shared_mutex
(withstd::shared_lock
) instead ofstd::mutex
. - Avoid 'find' called with a string literal consisting of a single character. Use the more effective overload accepting a character ex: find('a')
- Use a for-range loop variable's as a reference type as many times as possible to avoid copying.