This document lists C++11 coding recommendations common in the C++ development community. While recommendations make use of the C++11 language standard, they do not provide comprehensive guidance for use of all C++11 features. These recommendations are forward compatible with C++14.
This document was derived from C++ Programming Practice Guidelines. The recommendations are based on established standards collected from a number of sources, individual experience, local requirements/needs, as well as recommendations given in [1] - [11].
The recommendations given in this document are based on writings by Scott Meyers (see [3], [8] - [10]), as well as other sources.
The recommendations are grouped by topic and each recommendation is numbered to make it easy to refer to during code reviews.
Layout of the recommendations is as follows:
| Recommendation short description. |
| Illustrative code (as applicable). |
| Motivation, background and additional information. |
In the guideline sections the terms should and can have special meaning. A should is a strong recommendation, and a can is a general guideline.
| . If constexpr is not used, const should be used wherever possible. |
| A member function that does not affect the state of an object is to be declared const. These are the only functions which may be invoked on a const object. |
| . Implement a unique include guard in header files. If a collision occurs, then add namespace information. |
| #ifndef STRING_H // patterned after file name #define STRING_H // ... #endif // STRING_H #ifndef UTILITY_XML_STRING_H // patterned after namespace and file name #define UTILITY_XML_STRING_H // to overcome a collision // ... #endif // UTILITY_XML_STRING_H |
| Prevent compile-time multiple definition errors. |
| . Minimize use of unnamed namespaces. Prefer use of private class members to limit scope. Can use unnamed namespaces only in source files (.cpp) to define translation unit scoped items (reduces namespace pollution). |
| #include <iostream> namespace { int variable; void fnc (int); } namespace { void fnc (int i) { std::cout << i << std::endl; } } int main() { fnc(variable); return 0; } |
| Provides internal linkage without using the static keyword; does not create global scoped items. |
| . Use scoped enumerations. |
| enum class Animal // "class" keyword prevents implicit casting { CAT, DOG, RABBIT }; |
| Exclusion of the keyword class results in an unscoped enumeration that allows implicit casting to an integral type. |
| . Use the iostream library instead of the stdio library. |
| #include <iostream> // do not use: #include <stdio.h> |
| The stdio has been replaced by the much more powerful iostream library, and when programming C++ the latter should be preferred. |
| . C++ style casts should be preferred to C style casts. |
| static_cast<double> distance_m; // do not use: (double) distance_m |
| Adhere to common C++ programming practices. |
| . Only use auto declarations for iterators and control loops. Otherwise, use explicit declarations. |
| Explicit declarations reduce the risk introducing of type-related errors. |
| . Prefer const_iterators to iterators when not modifying values during iteration. |
| std::vector<int32> values; ... auto it = std::find(values.cbegin(),values.cend(), 1983); // use cbegin and cend values.insert(it, 1998); |
| Reduce likelihood of unintended modification of values. |
| . Prefer braced initialization. |
| int32 x{ 0 }; |
| Braced initialization is the most widely usable initialization syntax; it prevents narrowing conversions. |
| . Prefer references over pointers. |
| // function with constant string parameter (pass by reference) - preferred implementation void setMessage(const std::string& message) { ... } // function with constant string parameter (pass by pointer) - minimize as viable void setMessage(const std::shared_ptr<std::string> message) { ... } |
| If it is possible for the referred object to be null, then use smart pointers. |
| . Use shared smart pointers; do not use native pointers. Use unique smart pointers and weak smart pointers to effect and explicitly communicate associated scope limitations. |
| std::unique_ptr<std::vector<int32>> rowIndex = std::make_unique<std::vector<int32>>(10, 1); // 10 elements, each having value of 1 std::shared_ptr<std::vector<int32>> columnIndex = std::make_shared<std::vector<int32>>((10, 1); // 10 elements, each having value of 1 |
Smart pointers were introduced in C++11 with intent of reducing pointer-related errors. They provide:
|
| . Prefer using std::make_unique and std::make_shared functions for creating std::unique_ptrs and std::shared_ptrs, respectively. The only exception is for cases when custom delete functions are necessary or the passing of braced initializers is important to implementation. In these cases only, use the new instead of the make functions. For C++11 (only), the template shown in the code example should be added [3]. |
| // add make_unique function missing from C++11 (but included in C++14) template<typename T, typename... Args> std::unique_ptr<T> make_unique(Args&&... params) { return std::unique_ptr<T>(new T(std::forward<Args>(params)...)); } // create base class unique smart pointer to a derived class object std::unique_ptr<MyBaseClass> baseClass_up = std::make_unique<MyDerivedClass>(); // create base class shared smart pointer to a derived class object std::shared_ptr<MyBaseClass> baseClass_sp = std::make_shared<MyDerivedClass>(); |
| Using std::make_unique and std::make_shared provide stronger exception safety, greater protection against resource leakage, cleaner, simpler source code and smaller, faster object code [3]. |
| . Use nullptr for setting native pointers to null or passing in a null pointer parameter to a function. Do not use 0 or NULL. |
| string* myObject = nullptr; |
| Use of nullptr replaces use of 0 in C++11. NULL is part of the standard C library, but is made obsolete in C++. |
| . Generally avoid variable argument lists (...). |
| Variable argument lists prohibit strong type checking as provided by C++. In most cases variable argument lists can be exchanged by function overloading and by using default arguments. |
| . new and delete should be preferred over malloc, realloc and free. |
| In C++ there is no need for the older memory management functions malloc, realloc and free. It enhances readability of the code to use one consistent set of memory management methods. Also it makes deallocating memory safer, since it is dangerous to do delete on a malloc'ed object or free on a new'ed object. |
| . Explicitly define or delete copy/move constructors, operators and functions instead of implementing private "empty" constructors, operators and functions. |
| bool isEven(int number); // required function bool isEven(char) = delete; // prevent invoking isEven with char parameter bool isEven(bool) = delete; // prevent invoking isEven with bool parameter bool isEven(double) = delete; // prevent invoking isEven with double or float parameter MyClass(const MyClass& other) = delete; // prevent copy construction MyClass& operator=(const MyClass& other) = delete; // prevent copy assignment operation MyClass(const MyClass&& other) = delete; // prevent move construction MyClass& operator=(const MyClass&& other) = delete; // prevent move assignment operation |
|
Intent is preventing implementation/invocation of unwanted constructors, operators and functions. Note that in some cases, compilers generate constructors and assignment operators. By declaring such methods private and skipping their definition, attempts within code to call them will be trapped by the compiler. Methods that are implicitly generated by the compiler if they are not explicitly defined are:
|
| . Copy/move constructors and copy/move assignment operators should always be defined or deleted for classes with dynamically allocated memory ([8], Item 11). |
| MyClass(const MyClass& other) = delete; // prevent copy construction MyClass& operator=(const MyClass& other) = delete; // prevent copy assignment operation MyClass(const MyClass&& other) = delete; // prevent move construction MyClass& operator=(const MyClass&& other) = delete; // prevent move assignment operation |
| Unless a copy/move constructor or a copy/move assignment operator is explicitly defined or deleted, the compiler will generate them automatically. If the class uses dynamically allocated memory, the default generated copy/move constructors and copy/move assignment operators often do not behave as desired (e.g., memory management). For this copy case, a default implementation may suffice when multiple objects of the same class indeed should share a common data allocation. In this case, it should be defined via the = default implementation. Additionally, it is necessary to make sure that the shared data is not deallocated as long as there are references to it via use of smart pointers. |
| . The assignment operator should always return a reference to *this. It should always check for assignment to self. |
| MyClass& MyClass::operator= (const MyClass& rhs) { if (this != &rhs) { ... } return *this; } MyClass& MyClass::operator= (const MyClass& rhs) { if (*this != rhs) { ... } return *this; } |
| This assignment operator implementation mimics the assignment of built in types. |
| . Use the C++ keyword explicit for constructors callable with one argument. Applies to constructors where every parameter after the first has a default value ([4]). |
| Prevent errant implicit type conversions. |
| . Prefer simple construction and initialization. Minimize creation logic in constructors. Constructors may contain simple creation logic (e.g., initialization of data members). Constructors should not contain (i) moderate/complex creation logic; (ii) virtual function invocations; (iii) attempts to raise non-fatal failures. |
Complex initialization in constructors (in particular, initialization that can fail or that requires virtual method calls) has the following risks [8]:
|
| . The destructor should be virtual if and only if the class contains virtual methods ([8], Item 14). |
|
When delete is used on a derived object where the base class
destructor is not virtual, only the base class destructor will be called.
On the other hand, if a class does not contain any virtual members, it should not be used as a base class at all. In these cases it does not make sense to declare the destructor virtual both because it is not needed and because it will increase objects of the class with a completely unnecessary vptr and an associated vtbl. |
| . Use noexcept when declaring functions that will not throw exceptions. |
| void doesNotThrowExceptionFunction(int32 x) noexcept; |
| Provides more interface specification information and facilitates compiler optimization. |
| . Both == and != should be implemented if one of them is needed. |
| bool C::operator!= (const C& lhs) { return !(this == lhs); } |
| In most cases the != operator can be derived from the == operator as shown in the example. This is not automatically done by C++ as one could expect however. |
| . Operators &, &&, || and , (comma) should never be overloaded ([9], Item 7). |
|
Problem with user defined versions of these operators is that they will
not get the same behavior as the default versions for the standard types.
C++ employs a short-circuit evaluation of boolean expressions which means that as soon the truth or falsehood of an expression has been determined, evaluation of the expression ceases. There is no way this behavior can be transferred to the user defined operator methods. |
| . The return type of a function should always be specified explicitly. |
| int32 function() // do not use: function() { ... } |
| Functions, for which no return type is explicitly declared, implicitly receive int as the return type. Functions should never depend on this fact. |
| . For functions that can be implemented using a class' public interface, prefer implementing them as non-members. |
| Loosen coupling and increase class encapsulation [10]. |
| . Prefer using constant (const) pass-by-reference over pass-by-reference function parameters. Prefer pass-by-reference over pass-by-value function parameters. |
| myMethod (const SomeClass& object) const // preferred over myMethod (SomeClass& object) and myMethod (SomeClass object) |
|
There are two reasons for this. First is of performance.
Pass by value for objects always involves creating a temporary object using
the copy constructor of the objects class and destroying the object again
on the method exit.
Second reason is that objects passed by value through a base-class variable will in effect behave as a base-class object without the extended information defined by the derived class. |
| . A public member function should never return a non-const reference or pointer to a private member data ([11], Rule 29). |
| Returning a non-const reference to member data violates the encapsulation of the class. |
| .
Prefer declaring member variables as private over protected; prefer protected member declaration over public.
For the case of direct read/write member access by subclasses or other classes when encapsulation is not necessary, declare such members as protected or public to enable direct access; do not implement Get/Set accessor functions. For the case when read/write variable access should be controlled (encapsulation is required), declare member variables as private. Public access, if needed, should be through public Get/Set accessor functions. Subclasses should also use these same public Get/Set accessor functions. A base class can have protected non-const get functions that enable subclasses to read/write/modify base class private members. Reference: get/set style rule. |
| // // example - no Get/Set accessor functions since encapsulation not needed // class MyBaseClass { protected: std::vector<std::string> messages { "first_message", "second_message" }; public: int32 totalCount { 0 }; }; class MyDerivedClass : public MyBaseClass { public: void PerformWork() { int32 totalSize { 0 }; for (std::vector<std::string>::iterator msgIt = messages.cbegin(); msgIt != messages.cend(); msgIt++) { totalSize += msgIt->size(); } // can modify since a protected member messages.clear(); // setting protected variable via direct assignment messages = m_workMessages; }; private: std::vector<std::string> m_workMessages{ "message_A", "message_B" }; }; int main() { MyDerivedClass derivedInstance; derivedInstance.totalCount = 1; } |
| // // example with Get/Set accessor functions that effect encapsulation // class MyBaseClass { // public Get/Set accessors functions public: // upper camel case public Get accessor function const std::vector<std::string>& GetMessages() const { return (m_messages); }; // upper camel case public Set accessor function void SetMessages(std::vector<std::string>& messages) { m_messages = messages; }; // protected get accessor function protected: // lower camel case protected get accessor function (enables read/write of private member) std::vector<std::string>& getMessages() { return (m_messages); }; private: // private - so only this class can directly modify std::vector<std::string> m_messages { "first_message", "second_message" }; }; class MyDerivedClass : public MyBaseClass { public: void PerformWork() { int32 totalSize { 0 }; // cannot modify since accessing via public, const GetMessages() function for (std::vector<std::string>::iterator msgIt = GetMessages().cbegin(); msgIt != GetMessages().cend(); msgIt++) { totalSize += msgIt->size(); } // can modify since accessing via protected, non-const function getMessages().clear(); // setting private variable via public set accessor function SetMessages(m_workMessages); }; private: std::vector<std::string> m_workMessages{ "message_A", "message_B" }; }; |
| Adhere to common object-oriented programming practices. |
| . "Is-a" relationship should be modelled by inheritance, "has-a" relationship should be modelled by composition. |
| class B : public A // B "is-a" A { ... } class B { ... private: A a_; // B "has-a" A } |
| Adhere to common object-oriented programming practices. |
| . The assignment operator of a derived class should explicitly perform the assignment of its base class. |
| Derived& Derived::operator (const Derived& rhs) { if (this != &rhs) { Base::operator= (rhs); ... proceed with Derived assignments ... } return *this; } |
|
The base class assignment is never automatically called as one could
believe. Some compilers will not accept the above construct if the
assigment operator of the base class is automatically generated.
In this case use: static_cast which downcasts this to its base class and force the assignment through this base class reference. |
| . Use the keyword override to explicitly override virtual functions. |
| // example usage class MyBaseClass { public: // base class virtual function virtual void performWork(); ... }; class MyDerivedClass : public MyBaseClass { public: // overriding derived class virtual function virtual void performWork() override; ... }; |
| Absence of the keyword override results in a function in a derived class that does not override the base class function. Issue occurs when function is invoked as object is referenced as a base object. |
| . Non-virtual methods should never be redefined by a subclass. Redefinition does not implement overriding. |
|
There are two reasons for this. First is that if the method needs
to be redefined, then the subclass should not inherit from the base class
in the first place. It fails to be an "is-a" of the base class.
Then there is a technical reason. A non-virtual function is statically bound and a reference to the base class will always invoke the method of the base class even if the object is derived from the base class. |
| . Inherited default parameters should never be redefined. |
| virtual uint32 getDefaultLength_m() { return 11; }; // base class function uint32 getDefaultLength_m() { return 22; }; // overriding function in derived class |
| This is related to the previous rule. ... if inherited default parameters need to be redefined, then the subclass should not inherit from the base class in the first place. It fails to be an "is-a" of the base class. |
| . Private and protected inheritance should be avoided. |
| class C // using inheritance is improper (class C : private B) { ... private: B b_; // good - use of class B by composition } |
| While public inheritance models an "is-a" relationship, private or protected inheritance doesn't model anything at all, but is purely a short-cut implementation construct to re-use some code found within the base class. This kind of code re-use is typically best Implemented through composition ("has-a" relationship). |
| . Processing that is specific to a derived class should be implemented in a overriding virtual function; avoid downcasting. |
| derivedClass = static_cast<DerivedClass> base; // avoid downcasting |
| In some cases, the need for downcasting reveals a design flaw. Generally, C++ code should not branch on the type of objects ("if object A is of type so-and-so do this, else do that"). Instead, implement this type of behavior using virtual functions. |
| . Singleton objects should be preferred to global variables. |
| // // SingletonExample.h (START) // #ifndef SINGLETON_EXAMPLE_H #define SINGLETON_EXAMPLE_H #include <cstdint> #include <string> namespace abc { namespace def { class SingletonExample { public: static SingletonExample* Instance(); // <editor-fold defaultstate="collapsed" desc="Get and Set Accessors"> static constexpr std::int32_t getCount() { return (s_Count); }; // </editor-fold> private: SingletonExample() { }; // private constructor - cannot be externally invoked SingletonExample(SingletonExample const&) = delete; // delete copy constructor void operator=(SingletonExample const&) = delete; // delete copy assignment operator static SingletonExample* s_Instance; static constexpr std::int32_t s_Count { 44 }; }; }; // namespace def }; // namespace abc #endif /* SINGLETON_EXAMPLE_H */ // // SingletonExample.h (END) // // // SingletonExample.cpp (START) // #include " SingletonExample.h" namespace abc { namespace def { // Static pointer used to ensure a single instance of the class. SingletonExample* SingletonExample::s_Instance = nullptr; /** This function is called to access the singleton instance. Calling the constructor publicly is not allowed. The constructor is private and is only called by this Instance function. */ SingletonExample * SingletonExample::Instance() { // Test if need first time/one time creation if (SingletonExample::s_Instance == nullptr) { s_Instance = new SingletonExample; } return s_Instance; } }; // namespace abc }; // namespace def // // SingletonExample.cpp (END) // // usage in code uint32_t count = abc::def::SingletonExample::Instance()->getCount(); |
|
The singleton approach solves the problem of the undefined order of
initialization of global objects which can result in objects referring
to other objects not yet initialized.
In general, the need to use global objects in C++ is uncommon. |
| . Exceptions should be caught by reference. |
| try { ... } catch (Exception& exception) { ... } |
| Reduced computational cost benefit from using object reference. |
[1] C++ Programming Practice Guidelines
[2] C++ Programming Style Guidelines
[3] Effective Modern C++, Scott Meyers - O'Reilly Media, 2014
[5] C++ Language
[6] C++ Reference
[7] IBM Knowledge Center C++ Reference
[8] Effective C++ Second Edition, Scott Meyers - Addison-Wesley 1998
[9] More Effective C++, Scott Meyers - Addison-Wesley, 1996
[10] How Non-Member Functions Improve Encapsulation, Scott Meyers - Dr. Dobb's Journal, February 2000
[11] Programming in C++, Rules and Recommendations, M. Henricson / E. Nyquist, 1992
html>