1. RAII ( Resource acquisition is initialisation ) : This one is the most important one as it is quite common to manage resources. Using RAII, the resource will be initialised in the constructor and will be released/destroyed in the destructor. Therefore resource management is bound to the lifecycle of the object. Below You can see a prototype example :
A good example for RAII is std::unique_ptr template class in C++ , which is a smart pointer. When the instantiated unique_ptr goes out of scope, the stored pointer is deleted.
As you execute this code, you will see “Custom dtor” output. Again another example is std::lock_guard from C++11 thread library :
It should be noted that it is also very useful when using exceptions since it is guaranteed that when an exception being thrown, the stack unwinding will occur which will call destructors of the objects in the scope.
When to use : It can be used whenever you have a resource such as a pointer to a heap memory , synchronisation objects or file handles. RAII idiom is especially useful with exception throwing code. When an exception is thrown , stack unwinding starts and all the destructors of the objects in the scope will be called. Finally, RAII is also used inside copy-swap and copy-on-write idioms.
2) Pimpl ( Pointer to IMPlementation ) : This one is also called as opaque pointer method. The idea is hiding implementation details. For example, libraries given to the clients need both wrapping and encapsulation :
When to use : This technique is used to hide implementation details and also to reduce the compilation time since the implementation details are moved to a source file. Therefore you avoid making changes in header files during development.
3) Noncopyable : This one is useful for objects that shouldn`t be copied such as devices. In C++ you can do this in 2 ways as below:
a) C++03 : By making copy constructor and assignment operator private :
b) C++11: By using delete keyword with copy constructor and assignment operator
When to use : If you have classes which are not supposed to be copied such a device, then you can derive your classes from a NonCopyable class.
4) Copy-swap & Non-Throwing Swap: Based on Rule of Three ( Rule of Five in C++11) , if you implement copy constructor and destructor, you will also need to implement the assignment operator. If you think about it, a copy constructor initialises/creates and a destructor destroys things. And actually assignment operator implementation will need to do both things and also a self-assignment test by using this pointer. Therefore to avoid unnecessary comparison and code duplication, you can use copy-swap idiom :
So basically a swap method needs to be implemented which performs the actual swap operation. And during that implementation, you can use the existing functionality of the class copy constructor and destructor. Additionally , below you can see an exception safe version of it :
This is called as non-throwing copy-swap as pointer swapping in C++ simply doesn`t throw exceptions.
When to use : Those idioms can be used for assignment operator implementations whenever you have a class that requires to implement Rule of Three ( Rule of Five in C++11 )
5) Virtual Constructor : In C++ polymorphism, you can have virtual methods with different return types as long as those types are hierarchical. This is called covariant return types. Imagine a situation like this in which you need to clone an object without knowing its type in run-time :
void foo ( Base* b)
Base* b2 = new Base(b);
Derived* d = new Derived;
Even though b looks like Base class, actually it is Derived class because of upcasting. However when we call the new Base for b2, this copy construction will not be polymorphic since C++ doesn`t support virtual constructors. Virtual constructor idiom addresses this problem :
Now we can change the function as below :
Base* b2 = b->clone();
// And we can use b2 as Derived object
When to use : When you need to clone/copy polymorphic objects in runtime , virtual constructors help you to do this without knowing their type.
6) Curiously Recurring Template Pattern : This is also called as “static polymorphism”. Unlike the virtual method based polymorphism , you can avoid your code`s not being inlined by using CRTP. Lets first look at a typical polymorphic sample :
And below, you can see template based version of it :
When to use : Particularly useful if you are working in a no-RTTI project or if you want to optimise your code which usedynamic polymorphism
7) Copy-on-write : This idiom provides us a way to make copies (particularly deep copy operation with new operator over pointers ) when a write operation is needed. To be more explicit about a write operation, if you have a class with resource like a pointer, you will need a deep copy only if there is more than on object and if you write to other objects. As for the implementation, we will need to implement * and -> operators and a reference counter. By implementing * and -> operators , we will be able to detect external write operations and by using a reference counter, we will know if there is more than instances of the class. Both features are provided in std::shared_ptr , therefore we can use it for the implementation :
A famous example is QT library where this logic is called as “implicit sharing”. For a details you can see : http://qt-project.org/doc/qt-4.8/implicit-sharing.html
When to use : If you have a collection of object using some sort of resources and if “write” operation is quite rare , then copy-on-write can give you performance increase by avoiding deep copy costs.
8) Policy based design : If you know strategy design pattern , it allows you to change behaviour of algorithm. Policy based design can be described as compile time strategy pattern. The idea is your “host” template class can have as many as policies via inheritance. A straightforward description and simple code can be seen on Wikipedia : https://en.wikipedia.org/wiki/Policy-based_design
A nice example that uses that method is custom allocators in STL.
9) Empty Base Class : In C++ standards , every class instance has to have non-zero size in order to satisfy memory addresses and pointer arithmetic. This can lead to waste of memory. Also modern compilers will apply memory alignment , therefore an empty class instance will need minimum 4 bytes. However , if a class derived from an empty base class , then compilers can optimise memory usage by flattening , therefore a derived class instance will use less memory. This can be particularly useful for techniques such as policy based design :
AND MORE IDIOMS
You can visit http://en.wikibooks.org/wiki/More_C%2B%2B_Idioms to see a list of C++ idioms and their explanations.