Good Practice
Variables
Declare variables on first use, do not place them on top of a function body like in C.
Functions
Put output arguments before input arguments:
// Right void print (String& result, int v); // Wrong void print (int v, String& result);
Try to keep function argument count to 5 or fewer; consider structs or flags instead of multiple booleans.
// Right
struct PrintOptions
{
bool bold;
bool italic;
bool underline;
int fontSize;
String fontName;
};
void printText (StringRef text, const PrintOptions& options);
// Wrong
void printText (StringRef text, bool bold, bool italic,
bool underline, int fontSize, StringRef fontName);
Naming
Follow the Get Rule: If function names start with “get”, a returned object is not owned by the caller. Use “create”, “copy”, or any other more specific verb to indicate ownership.
// Right
SomeClass* getObject ()
{
return someObject;
}
// Wrong
SomeClass* getObject () // Use createObject () instead
{
return new SomeClass ();
}
SomeClass* getObject () // Use copyObject () instead
{
return new SomeClass (someObject);
}
Const
Omit const for local variables by default, unless it makes the code easier to understand.
// Right int temporaryVariable = getValue (); useValue (temporaryVariable); for(int value : values) { ... } // Wrong const int temporaryVariable = getValue (); useValue (temporaryVariable); for(const int value : values) { ... }
Omit const for local pointers.
// Right SomeClass* object = Factory::createObject (); const SomeClass* immutableObject = Factory::getImmutableObject (); for(SomeClass* item : myVector) { ... } // Wrong SomeClass* const object = Factory::createObject (); const SomeClass* const immutableObject = Factory::getImmutableObject (); for(SomeClass* const item : myVector) { ... }
Use const for function declarations by default.
// Right int getValue () const; void setValue (const Object&); // Wrong int getValue (); void setValue (Object&);
Control Statements
Case ‘default’ must not be the first or only case in a switch statement:
// Right switch(a) { case kCase1 : // ... break; case default : // ... } // Wrong switch (a) { default : // ... break; case kCase1 : // ... } // Wrong switch (a) { default : // ... break; }
No empty ‘default’ cases in switch statements:
// Right switch(a) { case kCase1 : // ... break; } // Wrong switch (a) { case kCase1 : // ... break; default: break; }
In conditional statements, put the constant term on the right side of the comparison (i.e. avoid the Yoda Condition):
// Right if(x >= 0) // Wrong if(0 <= x)
Pointers
Check for null pointers but don’t get too crazy: If you have just created the object a few lines above you are safe.
Consider using a reference over a pointer:
Use
Type&instead ofType*if null is not expected anyway.Return const references from get functions instead of pointers.
Use
nullptrover0orNULLfor unitialized state.
Spacing
Do not align assignments:
// Right int shortName = 1; int longerVariableName = 2; // Wrong int shortName = 1; int longerVariableName = 2;
Operators
Use postfix increment operator unless prefix increment is absolutely necessary:
// Right for(int i = 0; i < 10; i++) // Wrong for(int i = 0; i < 10; ++i)
Casts
Prefer C++ style casts for non-primitive types:
// Right SomeEnum value = static_cast<SomeEnum> (intVariable) // Wrong SomeEnum value = (SomeEnum) intVariable;
Prefer C++ style casts for pointer types:
// Right char* value = reinterpret_cast<char*> (data) // Wrong char* value = (char*) data;
Use function style cast for primitive types:
// Right double value = double(expr); // Wrong double value = static_cast<double> (expr);
String Constants
When declaring string constants in namespaces, use
const CStringPtr. Details:CStringPtris a pointer to constant data,const CStringPtris a const pointer to const datastaticis not required here, as const variables have internal linkage by default in a C++ namespace scope
namespace MyStringConstants { const CStringPtr kSomeConstant = "someConstant"; }
When declaring string constants in a class definition, use
static const CStringPtrorDECLARE_STRINGID_MEMBER:Use
static const CStringPtrfor simple constantsUse
DECLARE_STRINGID_MEMBERfor use ofCStringclass methods
class MyClass { public: MyClass (); DECLARE_STRINGID_MEMBER (kStringConstant) protected: static const CStringPtr kAnotherStringConstant; };
auto
Do use the
autokeyword to simplify code for the reader, particularly in context of lambdas and templates.Don’t use the
autokeyword to make code simpler to write.When assigning return values from API methods, use an explicit type to make the code easy to understand:
It is ok to use
autofor temporary variables that are only used to pass values to other methods.
// Right ISpecificInterface* instance = someObject->getSomething (); // Wrong auto* instance = someObject->getSomething ();
You may use the
autokeyword to avoid redundancy:// Right auto* implementation = unknown_cast<MyImplementationClass> (instance); // Also right MyImplementationClass* implementation = unknown_cast<MyImplementationClass> (instance);
Prefer
auto*andauto&overautowhen assigning pointer or reference types.Note that pointers are auto-deducted, but references are not. Using
autoinstead ofauto&may lead to unwantend copies.
// Right auto* implementation = unknown_cast<MyImplementationClass> (instance); // Wrong auto implementation = unknown_cast<MyImplementationClass> (instance);
Do use the
autokeyword when defining lambdas:// Right auto someLambdaFunction = [&] () { ... }
Do use the
autokeyword to simplify templates.// Right template <typename T> void templateMethod (const T& item) { auto internalData = item.getData (); ... } // Wrong template <typename T> void templateMethod (const T& item) { typedef decltype(T::data) InternalType; InternalType internalData = item.getData (); ... }
Prefer typedef declarations over using
autoin non-template classes// Right class DataManager { ... typedef LinkedList<InternalType> DataList }; ... DataManager::DataList& list = instance.getItems (); // Wrong auto& list = instance.getItems (); // Wrong LinkedList<DataManager::InternalType>& list = instance.getItems ();
Macros
Only add a semicolon after a macro if the macro requires it; avoid unnecessary semicolons.
// Right ASSERT (x == 0) // Wrong ASSERT (x == 0);
Classes
Ensure constructors init class state.
Do not perform work in constructors, use post-construction methods like
initialize()orload()instead.
// Right
class MyClass
{
public:
MyClass ()
{
// initialize members only
}
void initialize ()
{
// perform setup or resource loading here
}
};
// Wrong
class MyClass
{
public:
MyClass ()
{
// performs setup or resource loading here
}
};
Other
Use int as general purpose type in for-loops
Assumed to be 32 bit on all supported platforms.
All container classes use this type.
Do not use int32, unsigned int, short, unless you have a very good reason.
Do not pollute the global namespace, use namespaces per framework, product, etc.
Use C++ constant definitions instead of macros for constant values - unless you have a very good reason. Motivation: Macros are evaluated by the preprocessor and therefore do not follow namespaces.
Use C++ templates instead of macros.
Avoid anonymous enums because they would show up unnamend in Doxygen documentation.
Use assignment operator to initialize simple types:
// Right int x = 3; // Wrong int x (3);