¿Cómo utilizar el lenguaje Qt's PIMPL?

PIMPL significaPointer aIMPLementación La implementación significa "detalle de implementación": algo de lo que los usuarios de la clase no deben preocuparse.

Las implementaciones de clase propias de Qt separan limpiamente las interfaces de las implementaciones mediante el uso del lenguaje PIMPL. Sin embargo, los mecanismos proporcionados por Qt no están documentados. ¿Cómo usarlos?

Me gustaría que esta sea la pregunta canónica sobre "cómo hago PIMPL" en Qt. Las respuestas deben estar motivadas por una simple interfaz de diálogo de entrada de coordenadas que se muestra a continuación.

La motivación para el uso de PIMPL se hace evidente cuando tenemos algo con una implementación semi-compleja. Se da más motivación enesta pregunta. Incluso una clase bastante simple tiene que incluir muchos otros encabezados en su interfaz.

La interfaz basada en PIMPL es bastante limpia y legible.

// 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)

Una interfaz basada en Qt 5, C ++ 11 no necesita elQ_PRIVATE_SLOT línea.

Compare eso con una interfaz que no sea PIMPL que oculta los detalles de implementación en la sección privada de la interfaz. Tenga en cuenta cuánto debe incluirse otro código.

// 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)

Esas dos interfaces sonexactamente equivalente en lo que respecta a su interfaz pública. Tienen las mismas señales, ranuras y métodos públicos.

Respuestas a la pregunta(1)

Su respuesta a la pregunta