Recommendations#
CLASS DEFINITION RULES#
Class methods definitions SHOULD be in the same order as declarations#
Methods should be grouped by using order, not by alphabetical order. Code should be easy readable from top to down. Perfect case is method A uses method B and definition of method B follows definition of method A.
class Object
{
int getIntegerValue() const;
bool getBooleanValue() const;
};
int
Object::getIntegerValue() const
{
return getBooleanValue() ? 5 : 7;
}
bool
Object::getBooleanValue() const
{
return false;
}
Class fields SHOULD be initialized by the same order as they are declared#
Actual fields initialization order matches declaration order, but not definition order. It is important what order of initialization of class fields rather order of definition.
class Object
{
Object();
int m_integerValue;
bool m_booleanValue;
};
Object::Object()
: m_integerValue{5}
, m_booleanValue{false}
{}
Prefer default specifier to empty body#
Do not replicate compiler’s work for special class members.
// Use this
class Object
{
Object() {}
virtual ~Object() {}
};
// Instead of this
class Object
{
Object() = default;
virtual ~Object() = default;
};
Class methods and fields SHOULD NOT be mixed#
It is hard to understand what data is used here and where to add new fields to respect alignment.
// Use this
class Object
{
Object();
int getIntegerValue() const;
int m_integerValue;
bool m_booleanValue;
};
// Instead of this
class Object
{
int m_integerValue;
Object();
bool m_booleanValue;
int getIntegerValue() const;
};
Pay attention on alignment when add new field to class#
Take into account aggregates and inheritance. Don’t forget about Empty Base Class Optimization.
// Use this
class Object
{
int m_integerValue; // 4
bool m_booleanValue1; // 1
bool m_booleanValue2; // 1 (+2)
}; // = 8
// Instead of this
class Object
{
bool m_booleanValue1; // 1 (+3)
int m_integerValue; // 4
bool m_booleanValue2; // 1 (+3)
}; // = 12
Add virtual destructor for base entity class#
Warning is reported (if enabled) in case of class has virtual method but does not have virtual destructor. Destructor is not virtual by default.
class Object
{
virtual ~Object() = default;
};
Use override specifier for virtual methods#
Special keywords override
and final
can be used to force compiler check that method overrides some base virtual method.
No virtual
keyword is required to override method.
class Object
{
virtual ~Object() = default;
virtual int doAction1() = 0;
virtual int doAction2() = 0;
};
// Use this
class ObjectImpl
: public Object
{
~ObjectImpl() override;
int doAction1() override;
int doAction2() final;
};
// Instead of this
class ObjectImpl
: public Object
{
/*virtual*/ ~ObjectImpl();
/*virtual*/ int doAction1();
/*virtual*/ int doAction2();
};
Inline class methods SHOULD be defined outside class definition#
Do not mix implementation and declaration. Even if function body contains only single line.
// Use this
class Object
{
int doAction() const;
};
inline int
Object::doAction() const
{
return 5;
}
// Instead of this
class Object
{
int doAction() const
{
return 5;
}
};
Avoid inline
keyword in method declaration.
It should appear only in definition.
// Use this
class Object
{
int doAction() const;
};
// Instead of this
class Object
{
inline int doAction() const;
};
Minimize multiple inheritance#
Do not use multiple inheritance of implementations. Prefer to use either composition or base template classes.
// Use this
class ObjectImpl
: public BaseImpl2< BaseImpl1 >
// Instead of this
class ObjectImpl
: public BaseImpl1
, public BaseImpl2
Minimize multiple inheritance for entity interface hierarchy. It is recommended to have single inheritance branch to avoid excess memory cost. Each inheritance adds pointer to virtual table.
class Base1
{
virtual ~Base1() = default;
};
class Base2
{
virtual ~Base2() = default;
};
class EntityInterface
: public Base1
, public Base2
{};
// sizeof(EntityInterface) == sizeof(void*) * 2
Nevertheless it is allowed to use multiple inheritance for classes-controllers (mechanisms). Usually only a few objects of those classes are created to be used in program, so cost is invisible.
class EntityController
: public EventsListener
, public RichProvider
{};
Maximize const qualifier for methods-queries#
Provide const qualifier for methods-queries as much as possible. Write your code in a way to all queries should not modify object state to avoid unexpected side effects. Split operations by const queries and non-const commands.
class Object
{
int getValue() const;
};
Forbid copy/move operations for entity classes#
Explicitly forbid copy/move operations for entity class.
It is recommended to use boost::noncopyable
class as base to delete auto-generated copy/move constructors and assignment operators.
Use private inheritance type.
class Object
: private boost::noncopyable
{};
If you define or = delete
any default operation, define or = delete
them all.
Non-ASCII Characters#
Non-ASCII characters should be rare, and must use UTF-8 formatting.
You shouldn’t hard-code user-facing text in source, even English, so use of non-ASCII characters should be rare. However, in certain cases it is appropriate to include such words in your code. For example, if your code parses data files from foreign sources, it may be appropriate to hard-code the non-ASCII string(s) used in those data files as delimiters. More commonly, unittest code (which does not need to be localized) might contain non-ASCII strings. In such cases, you should use UTF-8, since that is an encoding understood by most tools able to handle more than just ASCII.
Hex encoding is also OK, and encouraged where it enhances readability - for example, "\xEF\xBB\xBF"
, or, even more simply, u8"\uFEFF"
, is the Unicode zero-width no-break space character, which would be invisible if included in the source as straight UTF-8.
Use the u8
prefix to guarantee that a string literal containing \uXXXX
escape sequences is encoded as UTF-8.
Do not use it for strings containing non-ASCII characters encoded as UTF-8, because that will produce incorrect output if the compiler does not interpret the source file as UTF-8.
You shouldn’t use char16_t
and char32_t
character types, since they’re for non-UTF-8 text.
For similar reasons you also shouldn’t use wchar_t
(unless you’re writing code that interacts with the Windows API, which uses wchar_t
extensively).
Structures appearance#
Structures should be used only to group data variables into logical group.
No access modifiers are allowed inside structures.
Generally structure should match Standard Layout concept.
Use class
to declare interfaces even if explicit public
specifier is required.