PIMPL означает P на IMPL ementation. Реализация означает "деталь реализации": что-то, чего не должны беспокоить пользователи класса.
Qt собственных реализаций класса четко отделяет интерфейсы от реализаций с помощью идиомы PIMPL. Тем не менее, механизмы, предоставляемые Qt, недокументированы. Как их использовать?
Мне бы хотелось, чтобы это был канонический вопрос о том, "как сделать PIMPL" в Qt. Ответы должны быть мотивированы простым диалоговым интерфейсом с координатами ввода, показанным ниже.
Мотивация использования PIMPL становится очевидной, когда у нас есть что-либо с полукомплексной реализацией. Дальнейшая мотивация дается в этом вопросе. Даже довольно простой класс должен задействовать множество других заголовков в своем интерфейсе.
Интерфейс на основе PIMPL довольно чистый и читаемый.
// CoordinateDialog.h
#include <QDialog>
#include <QVector3D>
class CoordinateDialogPrivate;
class CoordinateDialog : public QDialog
{
Q_OBJECT
Q_DECLARE_PRIVATE(CoordinateDialog)
#if QT_VERSION <= QT_VERSION_CHECK(5,0,0)
Q_PRIVATE_SLOT(d_func(), void onAccepted())
#endif
QScopedPointer<CoordinateDialogPrivate> const d_ptr;
public:
CoordinateDialog(QWidget * parent = 0, Qt::WindowFlags flags = 0);
~CoordinateDialog();
QVector3D coordinates() const;
Q_SIGNAL void acceptedCoordinates(const QVector3D &);
};
Q_DECLARE_METATYPE(QVector3D)
Интерфейс Qt 5, С++ 11 не нуждается в строке Q_PRIVATE_SLOT
.
Сравните это с интерфейсом, отличным от PIMPL, который заталкивает детали реализации в частный раздел интерфейса. Обратите внимание на то, как должен быть добавлен другой код.
// CoordinateDialog.h
#include <QDialog>
#include <QVector3D>
#include <QFormLayout>
#include <QDoubleSpinBox>
#include <QDialogButtonBox>
class CoordinateDialog : public QDialog
{
QFormLayout m_layout;
QDoubleSpinBox m_x, m_y, m_z;
QVector3D m_coordinates;
QDialogButtonBox m_buttons;
Q_SLOT void onAccepted();
public:
CoordinateDialog(QWidget * parent = 0, Qt::WindowFlags flags = 0);
QVector3D coordinates() const;
Q_SIGNAL void acceptedCoordinates(const QVector3D &);
};
Q_DECLARE_METATYPE(QVector3D)
Эти два интерфейса в точности эквивалентны по отношению к их общему интерфейсу. Они имеют одинаковые сигналы, слоты и общедоступные методы.