In the header file:
// widget.h
#include <memory> // std::unique_ptr
#include <experimental/propagate_const>
class Widget
{
public:
Widget();
~Widget();
void DoSomething();
private:
// the pImpl idiom is named after the typical variable name used
// ie, pImpl:
struct Impl; // forward declaration
std::experimental::propagate_const<std::unique_ptr< Impl >> pImpl; // ptr to actual implementation
};
In the implementation file:
// widget.cpp
#include "widget.h"
#include "reallycomplextype.h" // no need to include this header inside widget.h
struct Widget::Impl
{
// the attributes needed from Widget go here
ReallyComplexType rct;
};
Widget::Widget() :
pImpl(std::make_unique<Impl>())
{}
Widget::~Widget() = default;
void Widget::DoSomething()
{
// do the stuff here with pImpl
}
The pImpl
contains the Widget
state (or some/most of it). Instead of the Widget
description of state being exposed in the header file, it can be only exposed within the implementation.
pImpl
stands for "pointer to implementation". The "real" implementation of Widget
is in the pImpl
.
Danger: Note that for this to work with unique_ptr
, ~Widget()
must be implemented at a point in a file where the Impl
is fully visible. You can =default
it there, but if =default
where Impl
is undefined, the program may easily become ill-formed, no diagnostic required.