Ошибка множественного определения при создании частного подкласса в классе Qt Pimpl
Я пытался реализовать класс Pimpl, следуя инструкциям на этой вики-странице Qt, в которой закрытый класс наследуется от другого закрытого базового класса.
Вот основной пример:
Базовый класс
заголовок
// gadget.h
#ifndef GADGET_H
#define GADGET_H
#include <QWidget>
class GadgetPrivate;
class Gadget : public QWidget
{
Q_OBJECT
public:
explicit Gadget(QWidget *parent = 0);
~Gadget();
protected:
Gadget(GadgetPrivate &d, QWidget *parent = 0);
GadgetPrivate *d_ptr;
private:
Q_DISABLE_COPY(Gadget)
Q_DECLARE_PRIVATE(Gadget)
};
#endif // GADGET_H
Реализация
// gadget.cpp
#include "gadget.h"
#include "gadget_p.h"
Gadget::Gadget(QWidget *parent)
: QWidget(parent),
d_ptr(new GadgetPrivate(this))
{}
Gadget::~Gadget() {}
Gadget::Gadget(GadgetPrivate &d, QWidget *parent)
: QWidget(parent),
d_ptr(&d)
{}
Частный класс
// gadget_p.h
#ifndef GADGET_P_H
#define GADGET_P_H
#include "gadget.h"
class GadgetPrivate
{
Q_DECLARE_PUBLIC(Gadget)
public:
GadgetPrivate(Gadget *q);
Gadget *q_ptr;
};
GadgetPrivate::GadgetPrivate(Gadget *q)
: q_ptr(q)
{}
#endif // GADGET_P_H
Подкласс
заголовок
// gizmo.h
#ifndef GIZMO_H
#define GIZMO_H
#include "gadget.h"
class GizmoPrivate;
class Gizmo : public Gadget
{
Q_OBJECT
public:
explicit Gizmo(QWidget *parent = 0);
~Gizmo();
private:
Q_DISABLE_COPY(Gizmo)
Q_DECLARE_PRIVATE(Gizmo)
};
#endif // GIZMO_H
Реализация
// gizmo.cpp
#include "gizmo.h"
#include "gadget_p.h"
class GizmoPrivate : public GadgetPrivate
{
Q_DECLARE_PUBLIC(Gizmo)
public:
GizmoPrivate(Gizmo *q);
};
GizmoPrivate::GizmoPrivate(Gizmo *q)
: GadgetPrivate(q)
{}
Gizmo::Gizmo(QWidget *parent)
: Gadget(*new GizmoPrivate(this), parent)
{}
Gizmo::~Gizmo() {}
Я получаю следующую ошибку:
In function `GadgetPrivate::GadgetPrivate(Gadget*)':
error: multiple definition of `GadgetPrivate::GadgetPrivate(Gadget*)'
Кто-нибудь знает, что я здесь делаю не так?
2 ответа
Решение
Решение № 1:
Сделайте все в частном базовом классе встроенным, т.е. изменив это,
class GadgetPrivate
{
Q_DECLARE_PUBLIC(Gadget)
public:
GadgetPrivate(Gadget *q);
Gadget *q_ptr;
};
GadgetPrivate::GadgetPrivate(Gadget *q)
: q_ptr(q)
{
}
чтобы,
class GadgetPrivate
{
Q_DECLARE_PUBLIC(Gadget)
public:
GadgetPrivate(Gadget *q)
: q_ptr(q)
{
}
Gadget *q_ptr;
};
сделал код компиляции.
Решение № 2:
Это также, кажется, работает:
gadget_p.h
class GadgetPrivate
{
Q_DECLARE_PUBLIC(Gadget)
public:
GadgetPrivate(Gadget *q);
Gadget *q_ptr;
};
gadget.cpp
#include "gadget.h"
#include "gadget_p.h"
GadgetPrivate::GadgetPrivate(Gadget *q)
: q_ptr(q)
{
}
Gadget::Gadget(QWidget *parent)
: QWidget(parent),
d_ptr(new GadgetPrivate(this))
{
}
Gadget::~Gadget()
{
}
Gadget::Gadget(GadgetPrivate *d, QWidget *parent)
: QWidget(parent),
d_ptr(d)
{
}
Реализация GadgetPrivate
переехал в gadget.cpp
,
Это объявление конструктора:
Gadget(GadgetPrivate &d, QWidget *parent)
Требуется ссылка на GadgetPrivate
, который на данный момент является просто объявленным заранее классом. Переключитесь на тип параметра для указателя, и все будет в порядке:
Gadget(GadgetPrivate *d, QWidget *parent)
а также:
Gadget(GadgetPrivate *d, QWidget *parent)
: QWidget(parent), d_ptr(d)
{}
Некоторые заметки:
- Во-первых, используйте
QScopedPointer
для тебяd_ptr
, Это предотвратит утечку памяти, если вы не удалитеd_ptr
,q_ptr
должен оставаться необработанным указателем. - Этот Q&A на идиоме PIMPL является фантастическим и включает в себя множество подсказок о "гочах" и тому подобное.
- Марк Мутц написал несколько замечательных статей о Qt и идиоме pimpl. Их стоит проверить. Я связался со второй частью статей, потому что в ней более подробно рассказывается о внутренностях.