Qt中的Q_D以及Q_Q宏定义

在qt中,可以使用Q_DECLARE_PRIVATE配合Q_D来实现类似C++中PIMPL

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
// MainWindow.h
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include <QScopedPointer>
class MainWindowPrivate;
namespace Ui {
class MainWindow;
}
class MainWindow : public QMainWindow {
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = nullptr);
~MainWindow();
QString tag{"MainWindow"};
public slots:
void ClickedGetQPtrTag();
void ClickedGetDPtrTag();
protected:
const QScopedPointer<MainWindowPrivate> d_ptr;
private:
Ui::MainWindow *ui{nullptr};
Q_DECLARE_PRIVATE(MainWindow)
};
#endif // MAINWINDOW_H

Q_DECLARE_PRIVATE宏定义展开后为:

1
2
3
4
5
6
7
8
#define Q_DECLARE_PRIVATE(Class) \
inline Class##Private *d_func() { \
return reinterpret_cast<Class##Private *>(qGetPtrHelper(d_ptr)); \
} \
inline const Class##Private *d_func() const { \
return reinterpret_cast<const Class##Private *>(qGetPtrHelper(d_ptr)); \
} \
friend class Class##Private;

则代码中第29行展开后为:

1
2
3
4
5
6
7
8
#define Q_DECLARE_PRIVATE(MainWindow) \
inline MainWindowPrivate *d_func() { \
return reinterpret_cast<MainWindowPrivate *>(qGetPtrHelper(d_ptr)); \
} \
inline const MainWindowPrivate *d_func() const { \
return reinterpret_cast<const MainWindowPrivate *>(qGetPtrHelper(d_ptr)); \
} \
friend class MainWindowPrivate;

声明了两个返回d_ptr的指针的函数d_func,以及一个友元类MainWindowPrivate。
第6行的前置声明也有固定的名称组合class+Private,因为在宏定义中已经规定了声明的类名称
同样,第25行的d_ptr指针也是固定的名称,并且用了智能指针

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
// MainWindow.cpp
#include "mainwindow.h"
#include "ui_mainwindow.h"
class MainWindowPrivate {
public:
MainWindowPrivate(MainWindow *q) : q_ptr(q) {}
~MainWindowPrivate() = default;
MainWindow *const q_ptr;
QString GetQptrTag() const { return q_ptr->tag; }
private:
Q_DECLARE_PUBLIC(MainWindow)
QString tag{"MainWindowPrivate"};
};
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent), d_ptr(new MainWindowPrivate(this)),
ui(new Ui::MainWindow) {
ui->setupUi(this);
connect(ui->btn_1, &QPushButton::clicked, this,
&MainWindow::ClickedGetQPtrTag);
connect(ui->btn_2, &QPushButton::clicked, this,
&MainWindow::ClickedGetDPtrTag);
}
MainWindow::~MainWindow() { delete ui; }
void MainWindow::ClickedGetQPtrTag() {
Q_D(const MainWindow);
QString tag = d->GetQptrTag();
ui->label->setText(tag);
}
void MainWindow::ClickedGetDPtrTag() {
Q_D(const MainWindow);
QString tag = d->tag;
ui->label->setText(tag);
}

MainWindow.h中的前置声明在cpp实现,实现中需要注意的是Q_DECLARE_PUBLIC以及q_ptr
Q_DECLARE_PUBLIC的展开式为

1
2
3
4
5
6
#define Q_DECLARE_PUBLIC(Class) \
inline Class *q_func() { return static_cast<Class *>(q_ptr); } \
inline const Class *q_func() const { \
return static_cast<const Class *>(q_ptr); \
} \
friend class Class;

所以Q_DECLARE_PUBLIC(MainWindow)展开后为

1
2
3
4
5
6
#define Q_DECLARE_PUBLIC(MainWindow) \
inline MainWindow *q_func() { return static_cast<MainWindow *>(q_ptr); } \
inline const MainWindow *q_func() const { \
return static_cast<const MainWindow *>(q_ptr); \
} \
friend class MainWindow;

所以,需要在类中声明一个固定名称q_ptr的指针

在MainWindow中,则需要用到Q_D宏定义,展开后则为

1
#define Q_D(Class) Class##Private * const d = d_func()

Q_D(const MainWindow)展开后为:

1
MainWindowPrivate *const d = reinterpret_cast<const MainWindowPrivate *>(qGetPtrHelper(d_ptr));

所以在使用Q_D(const MainWindow)后,则可以用d指针对封装在MainWindowPrivate内的数据进行读取或者操作
注意,如果需要修改数据,则不应该增加const,应该为Q_D(const MainWindow)

MainWindowPrivate中,则可以使用Q_Q来获取Mainwindow的指针
Q_Q的展开为:

1
#define Q_Q(Class) Class * const q = q_func()

声明后则可以使用q来访问Mainwindow中的数据