Pimpl Idiom in C++
A C++ design method called the Pimpl idiom, which stands for "Pointer to Implementation". It is used to keep a class's implementation details hidden from its users. The "Compiler Firewall" or "Cheshire Cat" idioms are other names for this one. Reducing compilation dependencies and encapsulating intricate or shifting implementation details are just two of the frequent issues it tackles in software development.
The primary goal of the Pimpl idiom is to distinguish a class's secret implementation from its public interface. You specify a pointer to an opaque data structure (often a class) in the class header file, and you put the actual implementation details in a separate source file, rather than defining every member of the class in the class declaration. Because of this separation, you can modify the implementation without impacting code that depends on the class or requiring recompilation on users of the class.
Implementation of Pimpl Idiom:
- For implementation, create a different class (or struct).
- Add every private member found in the header to that particular class.
- Create an Implementation class (Impl) in the header file.
- Make a forward declaration, or pointer, in the header file that points to the implementation class.
- Define the copy/assignment operators and the destructor.
class my_class::impl {
// defined privately here
// all private data and functions: all of these
// change without recompiling callers
};
my_class::my_class(): pimpl( new impl )
{
// ... set impl values ...
}
Methods of Pimpl Idiom in C++
1. Implementation Class Private (Impl):
Create a private implementation class inside the class declaration, usually called Impl or a similar name.
2. A Hint for Execution:
Declare a private member variable in the public class that contains a pointer to an instance of the private implementation class.
3. Progression Proclamation:
In the header file of the public class, use a forward declaration of the private implementation class instead of the actual implementation class.
4. Build Master:
Allocate memory for the private implementation using new in the public class's constructor.
5. Destructor:
The private implementation's memory should be de-allocated using delete in the public class destructor.
6. Public User Interface:
Provide the constructors, public member methods, and destructors that make up the public interface of the class in the header file. These ought to communicate via the pointer with the private implementation.
7. Separation of Concerns:
Put all of the implementation specifics in the private implementation class, such as private data members and member functions. The public interface and the implementation are now distinct from one another.
8. Encapsulation:
Make sure private implementation information is contained and hidden from external code by using access specifiers.
Example:
Let's take an example to demonstrate the use of Pimpl Idiom in C++:
// MyPublicClass.h
class MyPublicClass
{
public:
MyPublicClass(); // Creation of Constructor
void DoSomething(); // Public member function
~MyPublicClass(); //Creation of Destructor
private:
class Impl; // The private implementation class of Forward declaration
Impl* pimpl; // Pointer to the private implementation
};
// Defining the private implementation class here
class MyPublicClass::Impl {
public:
void DoSomethingInternal() {
// Implementation process
}
};
#include "MyPublicClass.h" //Header file
#include<iostream.h> //Header file
// Constructor
MyPublicClass::MyPublicClass() : pimpl(new Impl) {
// Allocate memory for the private implementation
}
// Public member function
void MyPublicClass::DoSomething() {
pimpl->DoSomethingInternal();
}
// Destructor
MyPublicClass::~MyPublicClass() {
delete pimpl;
}
Output:
Doing something internally
Explanation of the above code:
- In this example, the private implementation class Impl is forward declared in the header files MyPublicClass.h, iostream.h along with a smart pointer (std::unique_ptr) to the private implementation. MyPublicClass is also declared in this file.
- MyPublicClass.cpp, the file containing the real implementation details (such the DoSomethingInternal function), defines the private implementation class Impl.
- Memory is allotted for the private implementation in the constructor of MyPublicClass using a smart pointer. When the MyPublicClass object gets destroyed, the smart pointer will take care of memory deallocation automatically.
- MyPublicClass's DoSomething member method assigns the task to the private implementation by using its DoSomethingInternal function.
Advantages of Pimpl Idiom in C++:
Several advantages of the Pimpl Idiom are as follows:
- Binary Compatibility: The private fields have no effect on the binary interface. The dependent code would not break if the implementation were modified.
- Compilation time: The compilation time of the file is reduced because only the implementation file needs to be rebuilt rather than each client recompiling his file.
Disadvantages of Pimpl Idiom in C++:
Several disadvantages of the Pimpl Idiom are as follows:
- Maintenance Effort: The new class to use pimpl and additional pointer indirection are making maintenance more complex. (Interface can be used only via pointer/reference)
- Inheritance: Though a class PImpl can be inherited, hidden implementation is not inheritable.
Conclusion:
In C++, the Pimpl (Pointer to Implementation) idiom is a potent design method that promotes improved code encapsulation and organization. It provides multiple benefits by providing a class's private implementation and public interface separate. First of all, it lessens the need for additional compilation when implementation details change, thus reducing compilation times.
The Pimpl idiom also encourages encapsulation since it makes it possible to conceal complex implementation details and keep them hidden from outside code. Code is simpler to comprehend, maintain, and modify as a result of the separation of concerns. Furthermore, the idiom makes binary compatibility easier, which is crucial for libraries and allows you to change and expand your code without affecting existing client code.