Идиома pimpl (pointer to implementation - указатель на реализацию) полезна в тех случаях, когда нам нужно что-то скрыть. Она обеспечивает еще более глубокий вид инкапсуляции, которая маскирует не просто реализацию, а также все ее зависимости.
Например, мы создаем библиотеку, которая сама зависит от сторонних библиотек. При этом мы собираемся передать нашу библиотеку кому-то другому без исходников. Чтобы не тянуть зависимости в виде заголовочных файлов (как минимум) и не раскрыть слишком много деталей реализации в интерфейсе класса (как максимум), мы можем воспользоваться идиомой pimpl.
Не будем усложнять пример. Рассмотрим лишь общую механику работы pimpl
в искусственной ситуации. Идиома является хорошо масштабируемой и без труда распространяется на значительно более сложные проекты.
Заголовочный файл myclass.h
:
#ifndef MYCLASS_H
#define MYCLASS_H
class MyClassImpl;
class MyClass {
public:
MyClass();
~MyClass();
int callMe( int arg );
private:
MyClassImpl* pimpl;
};
#endif // MYCLASS_H
Обратите внимание, что мы ссылаемся на класс реализации не через #include
, а с помощью объявления class MyClassImpl
. В этом случае мы можем создавать указатель на этот класс не имея заголовочного файла (который мы и не собираемся распространять).
Переходим к реализации (myclass.cpp
):
#include "myclass.h"
#include "myclassimpl.h"
#include <QDebug>
MyClass::MyClass() : pimpl( new MyClassImpl ) {
qDebug() << "MyClass ctor";
}
MyClass::~MyClass() {
qDebug() << "MyClass dtor";
delete pimpl;
}
int MyClass::callMe( int arg ) {
return pimpl->callMe( arg );
}
Здесь нам уже потребовалось подключить заголовочный файл для MyClassImpl
. Но эта часть относится к реализации, поэтому все в порядке. Вызов каждой открытой функции MyClass
делегируется соответствующей функции-члену реализации MyClassImpl
:
int MyClass::callMe( int arg ) {
return pimpl->callMe( arg );
}
Здесь все еще проще. Заголовочный файл myclassimpl.h
:
#ifndef MYCLASSIMPL_H
#define MYCLASSIMPL_H
#include <myclass.h>
#include <string>
class MyClassImpl {
public:
MyClassImpl();
~MyClassImpl();
int callMe( int arg );
private:
// Просто так. Чтобы было
int a;
std::string s;
};
#endif // MYCLASSIMPL_H
Реализация в myclassimpl.cpp
:
#include "myclassimpl.h"
#include <QDebug>
MyClassImpl::MyClassImpl() {
qDebug() << "MyClassImpl ctor";
}
MyClassImpl::~MyClassImpl() {
qDebug() << "MyClassImpl dtor";
}
int MyClassImpl::callMe( int arg ) {
return arg;
}
Файл main.cpp
:
#include <QDebug>
#include "myclass.h"
int main() {
MyClass a;
qDebug() << a.callMe( 5 );
return 0;
}
Основное здесь то, что компилятор не требует наличия файла myclassimpl.h
в контексте main.cpp
. А этого мы и хотели.
Скачать пример использования идиомы pimpl в C++