Ошибка множественного определения при создании частного подкласса в классе 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. Их стоит проверить. Я связался со второй частью статей, потому что в ней более подробно рассказывается о внутренностях.
Другие вопросы по тегам