General coding conventions

Type casting

Never use C-Style Casts
The C-style cast - "(type) expr" used to convert one fundamental type to another is subject to implementation-defined effects. For scalar types it can result in silent truncation of the value. For pointers and references, it does not check the compatibility of the value with the target type.
Don't cast away const, use mutable keyword instead
Don't reinterpret_cast, fix the design instead
Remember that reference cast will throw an error if cast fails
Avoid using pointer or reference casts. They have been referred to as the goto of OO programming, fix the design instead
X* ptr = static_cast<X*>( y_ptr ); // ok, compiler checks whether types are compatible (Foo*) ptr; // bad! C-cast is not guaranteed to check and never complains

Classes

A class interface should be complete and minimal. Class should encapsulate one thing and one thing only. A complete interface allows clients to do anything they may reasonably want to do. On the other hand, a minimal interface will contain as few functions as possible. Class methods must be defined in the same order as they are declared. This helps navigating through code.

Compulsory member functions

Every class must define default constructor, copy constructor, assignment operator and destructor. If you dont declare them, compiler will and the compiler generated versions are usually not good or safe enough. If your class does not support copying, then declare copy constructor and assignment operator as private and don't define them.

If for example the assignment operator is not needed for a particular class, then it must be declared private and not defined. Any attempt to invoke the operator will result in a compile-time error. On the contrary, if the assignment operator is not declared, then when it is invoked, a compiler-generated form will be created and subsequently executed. This could lead to unexpected results. The same goes with default constructor and copy constructor.

class X { X(); // default constructor X( const X& ); // copy constructor X& operator=( const X& ); // copy assignment operator ~X(); // destructor }; class X { X(); // default constructor ~X(); // destructor X( const X& ) = delete; // copy constructor not allowed X& operator=( const X& ); = delete; // copy assignment operator not allowed };

Class types

Classes can have either Value semantics or Pointer semantics. Not both. It must be clearly documented whether a class follows value or pointer semantics and this also sets requirements on the class interface.

Classes with Value semantics are passed as value types. These classes provide a copy constructor, a default constructor and assignment operator so that they can be copied and also stored on STL containers.

Classes with Pointer semantics are always passed through pointers, references or smart pointers. These classes are ususally compound types that cannot be easily copied and thus prevent copy constructor, and assignment operator. They can be only stored on STL containers through smart pointers.

Access Rights

Public and protected data should only be used in structs, not classes. Roughly two types of classes exist: those that essentially aggregate data and those that provide an abstraction while maintaining a well-defined state or invariant.

A structure should be used to model an entity that does not require an invariant (Plain Old Data) A class should be used to model an entity that maintains an invariant. Rationale: A class is able to maintain its invariant by controlling access to its data. However, a class cannot control access to its members if those members non-private. Hence all data in a class should be private.

Constructors

Virtual function calls are not allowed from constructor Rationale: Virtual functions are resolved statically (not dynamically) in constructor

Member initialization order must be the same in which they are declared in the class. Note: Since base class members are initialized before derived class members, base class initializers should appear at the beginning of the member initialization list. Rationale: Members of a class are initialized in the order in which they are declared—not the order in which they appear in the initialization list.

Constructor body should not throw an exception, keep constructor simple and trivial. If constructor fails, objects lifecycle never started, destructor will not be called.

Declare all single argument constructors as explicit thus preventing their use as implicit type convertors. class C { public: explicit C( int ); // good, explicit C( int, int ); // ok more than one non-default argument }; class C { public: C( double ); // bad, can be used in implicit conversion C( float f, int i=0 ); // bad, implicit conversion constructor C( int i=0, float f=0.0 ); // bad, default constructor, but also a conversion constructor };

Destructor

All classes should define a destructor, either:

This prevents undefined behavior. If an application attempts to delete a derived class object through a base class pointer, the result is undefined if the base class destructor is non-virtual.

Virtual function calls are not allowed from inside the destructor. Rationale: A class's virtual functions are resolved statically (not dynamically) in its destructor

All resources acquired by a class shall be released by the class's destructor.

Destructor is not allowed to throw an exception, avoid doing complicated things in destructor.

Methods

Don't shortcut, like use the returned reference of getter to assign a new value. If a Setter is missing, add it! initial.GetPosition() = Position(10, 10); // bad!, If GetPosition is one day changed to return copy // of Position this code silently changes to a no-op. initial.SetPosition( Position( 10, 10 ) );

Code that is not used (commented out) should be deleted. Rationale: No dead code should be left to confuse other people. Exception: Code that is simply part of an explanation may appear in comments.

Inline member functions

GCC automatically inlines member functions defined within the class body of C++ programs even if they are not explicitly declared inline. class Color { inline float& GetRed() { return mRed; } // inline keyword not needed inline float& GetGreen(){ return mGreen; } }; class Color { float& GetRed() { return mRed; } float& GetGreen(){ return mGreen; } };

If there are a lot of inlines, they should be in a .inl file. Remember the inline keyword is just a hint to the compiler. Whether a function will be inlined or not is down to the compiler and its flags.

Conversion operators

Don't declare implicit conversion operators in classes. They allow the compiler to trip you up and go from one type to another via the conversion operator unintentionally. Conversion operators are particularly dangerous in conjunction with auto keyword. If conversion is required, make it explicit or better yet, add a getter with a more meaningfull name. // Bad: class SmallInt { public: // implicit conversion to float operator float() const { return float( val ); } private: int val; }; //... and in the program: int main( void ) { int value; SmallValue foo; value = foo; // oops, didn't really want to allow conversion to int but the compiler can do that as float can be assigned to int. return 0; } // Good: class SmallInt { public: // explicit getter for float float AsFloat const { return static_cast<float>( val ); } private: int val; }; //... and in the program: int main( void ) { int value; SmallValue foo; si.AsFloat() + 3; // ok: explicitly request the conversion return 0; } // Good: class SmallInt { public: // explicit conversion to int explicit operator int() const { return val; } private: int val; }; //... and in the program: int main() { SmallInt si = 3; // ok: the SmallInt constructor is not explicit si + 3; // error: implicit is conversion required, but operator int is explicit static_cast<int>(si) + 3; // ok: explicitly request the conversion return 0; }

Auto keyword

auto keyword should only be used where it improves the readability of the code and does not lead to ambiguities. never use auto in a line where multiple different types occur as part of expressions like additions, subtracts, multiplies as whe conversion ordering rules are not always obvious. // Good: auto actor = Actor::DownCast(GetOwner()); // it is obvious that actor is of type Actor so no need to retype the type auto widthMode = widthMeasureSpec.GetMode(); // it is relatively obvious that Mode is an enumeration with potentially long name so no need to repeat the type, no ambiguity auto childLayout = GetChildAt( i ); // name of the variable is clear enough indication of the type, no ambiguity auto childPosition = childOwner.GetProperty< Dali::Vector3 >( Actor::Property::POSITION ); // getter already contains the type, no need to repeat it for ( auto&& renderTask : mImpl->taskList.GetTasks() ) // iterator type not relevant for the algorithm, code much cleaner with auto { renderTask->UpdateState(); } // Bad: auto width = layout->GetWidth() - padding.end - padding.start; // not obvious what the final type ends up as multiple type conversions may occur auto size = std::max( LayoutLength(0), specSize - padding ); // not obvious which of the types is preferred by compiler; or what the type of specSize - padding actually is auto minPosition = Vector3( Vector3::ZERO ); // auto does not add any value here // Good: Vector3 minPosition; // vector initializes to 0,0,0 // Bad: auto specification = MeasureSpec( GetMeasuredHeight(), MeasureSpec::Mode::EXACTLY ); // no value in typing auto in assignment, much cleaner and less ambiguous to write: // Good: MeasureSpec specification( GetMeasuredHeight(), MeasureSpec::Mode::EXACTLY ); // obvious construction of a type with parameters

General design priciples

Here's a few pragmatic programmer guidelines to follow (Web version)

Details

Avoid Tight Coupling

Always choose the loosest possible coupling between entities. In C++ the tightest coupling is Friend, second is Inheritance, then Containment and last is Usage through reference, pointer or handle.

Details

Most of the time containment through interface and normal usage is what you should go for. Strong ownership always beats sharing through reference counting. Reference counting means "part owns". You would not want to part own anything in real life, so why do that in software? sooner or later it will leak,

Two key principles to follow:

Open Closed Principle

Software entities (classes, modules, functions, etc.) should be open for extension, but closed for modification. That is, such an entity can allow its behaviour to be modified without altering its source code. Techniqu

Dependency Inversion Principle

High-level modules should not depend on low-level modules. Both should depend on abstractions. Abstractions should not depend upon details. Details should depend upon abstractions.

Thats all folks, if you read this far you are now all equipped to write good code :) !!