二进制兼容

QT的源文件中,包含许多Q_DQ_Q宏,这两个宏是d指针(d-pointer,也称不透明指针,the opaque pointer)的设计模式的一部分,其可以做到:

  1. 对用户隐藏库的实现细节;
  2. 实现二进制兼容(可以在不破坏二进制兼容性的情况下,对库进行实现的更改)。

    什么是二进制兼容?什么是源代码兼容?
    一个动态链接到旧版本库下运行的程序,在无需经过重新编译的情况下,仍然能够在新版本库下继续运行,这种库称为二进制兼容动态库
    一个动态链接到旧版本库下运行的程序,在不需要修改程序源代码,但需要经过重新编译的情况下,才可以在新版本的库下继续运行,这种库称为源代码兼容动态库;
    如何做到二进制兼容?
    要使得一个dllso能做到二进制兼容,就要求其中的每一个结构和每一个对象的数据模型保持不变(如增加或删除类的数据成员,就会影响对象的数据模型),若发生变动,其会导致原有的数据成员在对象数据模型中的位移发生变化,那么编译后的新版本库很大概率会使得程序运行崩溃。
    因此,为了使得增删类数据成员后对象数据模型也不会发生变化,通常有以下做法:

    • 预先分配若干保留空间以备用(做法死板,因为不知道未来需要扩展多少项,同时也浪费空间)
    • 将类A的数据成员放到另一个类B中,类A引入类B的一个指针(做法灵活,无论类B是否添加或删除了数据成员,类A中的数据成员始终都只有指向类B的指针的四个字节大小)
      QT中,为了实现二进制兼容,绝大多数类都是采用第二种做法。

d-pointer

参考:https://wiki.qt.io/D-Pointer

假设有2个类,分别为Widget类和Button类,其中Button类继承自Widget类。代码如下:

1
2
3
4
5
6
7
8
9
10
class Widget
{
public:
Widget();
~Widget();
int width();
void setWidth(int w);
private:
int width;
};
1
2
3
4
5
6
7
8
9
10
11
12
13
#include "Widget.h"
#include <iostream>

Widget::Widget()
{
std::cout << "Widget is created." << std::endl;
}
Widget::~Widget()
{
std::cout << "Widget is destroyed." << std::endl;
}
int Widget::width() { return width; }
void Widget::setWidth(int w) { width = w; }
1
2
3
4
5
6
7
8
9
10
class Button : public Widget
{
public:
Button();
~Button();
std::string text();
void setText(std::string t);
private:
std::string text;
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
#include "Button.h"
#include "ButtonPrivate.h"
#include <iostream>

Button::Button()
{
std::cout << "Button is created." << std::endl;
}
Button::~Button()
{
std::cout << "Button is destroyed." << std::endl;
}
std::string Button::text() { return text; }
void Button::setText(std::string t) { text = t; }

编译上述程序后,得到demoLib_1.0.dll
此时使用程序DemoApp调用Button::getText(),程序正常运行。
在下一个版本中,Widget类中将新增一个变量height:

1
2
3
4
5
6
7
8
9
10
11
12
13
class Widget
{
public:
Widget();
~Widget();
int width();
void setWidth(int w);
int height();
void setHeight(int h);
private:
int width;
int height;
};

编译上述程序后,得到demoLib_1.1.dll
此时使用程序DemoApp去调用Button::getText(),当程序未重新编译时,会导致程序崩溃。
为什么程序运行崩溃了?
Widget类添加了新数据成员,将改变Widget类和Button类对象的大小。
因为C++编译器生成代码时,使用偏移量来访问对象中的数据。
Button对象在两个版本的(简化)内存结构如下:

偏移量 0 1 2
demoLib_1.0 width text
demoLib_1.1 width height text
在1.0中,text位于偏移量1的逻辑位置,程序调用Button::getText(),返回偏移量为1的数据成员;
在1.1中,text位于偏移量2的逻辑位置,程序没有重新编译,使用旧的内存布局,调用Button::getText(),返回偏移量为1的数据成员,进而导致程序崩溃。

因此,当库文件发布后,不要改变导出的C++类的大小和数据成员位置。
那么,如何在不改变对象大小及成员位置的同时添加新功能呢?
为了使得库中被调用的公共类的内存布局不发生改变,我们可以让所有的公共类都只持有一个私有的指针变量,这个指针变量指向一个包含所有数据的私有类(或结构体等),该私有类可以在未来的版本中任意修改数据成员,而不影响到使用了这个公共类的程序。外部无法使用这个私有类,只能使用公有类,且公有类只持有一个指向私有类的指针变量,该指针就被称为D指针

  • Widget.h
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    class WidgetPrivate;
    class Widget
    {
    public:
    Widget();
    ~Widget();
    int width();
    void setWidth(int w);
    int height();
    void setHeight(int h);
    private:
    WidgetPrivate* d_ptr;
    };
  • Widget.cpp
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    #include "A.h"
    #include "Widget.h"
    #include <iostream>
    #include "WidgetPrivate.h"

    Widget::Widget() : d_ptr(new WidgetPrivate())
    {
    std::cout << "Widget is created." << std::endl;
    }
    Widget::~Widget()
    {
    if (d_ptr) { delete d_ptr; }
    std::cout << "Widget is destroyed." << std::endl;
    }
    int Widget::width() { return d_ptr->width; }
    void Widget::setWidth(int w) { d_ptr->width = w; }
    int Widget::height(){ return d_ptr->height; }
    void Widget::setHeight(int h) { d_ptr->height = h; }
  • WidgetPrivate.h
    1
    2
    3
    4
    5
    6
    7
    8
    9
    class WidgetPrivate
    {
    public:
    WidgetPrivate();
    ~WidgetPrivate();
    public:
    int width;
    int height;
    };
  • WidgetPrivate.cpp
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    #include "WidgetPrivate.h"
    #include <iostream>

    WidgetPrivate::WidgetPrivate() : width(100), height(40)
    {
    std::cout << "WidgetPrivate is created." << std::endl;
    }
    WidgetPrivate::~WidgetPrivate()
    {
    std::cout << "WidgetPrivate is destroyed." << std::endl;
    }
  • Button.h
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    #include <string>
    #include "Widget.h"

    class ButtonPrivate;
    class Button : public Widget
    {
    public:
    Button();
    ~Button();
    std::string text();
    void setText(std::string t);
    private:
    ButtonPrivate* d_ptr;
    };
  • Button.cpp
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    #include "Button.h"
    #include "ButtonPrivate.h"
    #include <iostream>

    Button::Button() : d_ptr(new ButtonPrivate)
    {
    std::cout << "Button is created." << std::endl;
    }
    Button::~Button()
    {
    if (d_ptr) { delete d_ptr; }
    std::cout << "Button is destroyed." << std::endl;
    }
    std::string Button::text() { return d_ptr->text; }
    void Button::setText(std::string t) { d_ptr->text = t; }
  • ButtonPrivate.h
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    #include <string>

    class ButtonPrivate
    {
    public:
    ButtonPrivate();
    ~ButtonPrivate();
    public:
    std::string text;
    };
  • ButtonPrivate.cpp
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    #include "ButtonPrivate.h"
    #include <iostream>

    ButtonPrivate::ButtonPrivate()
    {
    std::cout << "ButtonPrivate is created." << std::endl;
    }
    ButtonPrivate::~ButtonPrivate()
    {
    std::cout << "ButtonPrivate is destroyed." << std::endl;
    }
  • main.cpp
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    #include <iostream>
    #include "Button.h"

    int main()
    {
    Button btn;
    btn.setText("世界和平");
    std::cout << "****************************" << std::endl;
    std::cout << "btn.width: " << btn.width() << std::endl;
    std::cout << "btn.height: " << btn.height() << std::endl;
    std::cout << "btn.text: " << btn.text() << std::endl;
    std::cout << "****************************" << std::endl;
    return 0;
    }
  • 控制台输出
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    WidgetPrivate is created.
    Widget is created.
    ButtonPrivate is created.
    Button is created.
    ****************************
    btn.width: 100
    btn.height: 40
    btn.text: 世界和平
    ****************************
    ButtonPrivate is destroyed.
    Button is destroyed.
    WidgetPrivate is destroyed.
    Widget is destroyed.
    如此,调用该库的程序就无法直接访问到D指针,且私有类中的数据都可以随意更改。

d-pointer的优势

  • 二进制兼容;
  • 因此实现细节:只需发布库的头文件和二进制文件,源文件可以闭源;
  • 可作为API:头文件没有任何实现的细节,可以作为API;
  • 编译速度更快:头文件中的实现细节都被移动到源文件中,加快了编译速度。

q-pointer

目前,公共类的D指针指向了私有类,在实际应用中,私有类中的私有函数通常需要调用公共类的属性或方法,为了解决这个问题,此时我们也需要在私有类中引入一个指向该公有对象的指针,该指针被称为Q指针

  • Widget.h
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    class WidgetPrivate;
    class Widget
    {
    public:
    Widget();
    ~Widget();
    int width();
    void setWidth(int w);
    int height();
    void setHeight(int h);
    // 添加了一个新的示例函数
    void update();
    private:
    WidgetPrivate* d_ptr;
    };
  • Widget.cpp
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    #include "Widget.h"
    #include <iostream>
    #include "WidgetPrivate.h"

    Widget::Widget() : d_ptr(new WidgetPrivate)
    {
    d_ptr->q_ptr = this;
    std::cout << "Widget is created." << std::endl;
    }
    Widget::~Widget()
    {
    if (d_ptr) { delete d_ptr; }
    std::cout << "Widget is destroyed." << std::endl;
    }
    int Widget::width() { return d_ptr->width; }
    void Widget::setWidth(int w) { d_ptr->width = w; }
    int Widget::height(){ return d_ptr->height; }
    void Widget::setHeight(int h) { d_ptr->height = h; }
    void Widget::update()
    {
    std::cout << "Widget::update() is running." << std::endl;
    }
  • WidgetPrivate.h
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    class Widget;
    class WidgetPrivate
    {
    public:
    WidgetPrivate();
    ~WidgetPrivate();
    public:
    Widget* q_ptr;
    int width;
    int height;
    };
  • WidgetPrivate.cpp
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    #include "WidgetPrivate.h"
    #include "Widget.h"
    #include <iostream>

    WidgetPrivate::WidgetPrivate() : width(100), height(40)
    {
    std::cout << "WidgetPrivate is created." << std::endl;
    }
    WidgetPrivate::~WidgetPrivate()
    {
    std::cout << "WidgetPrivate is destroyed." << std::endl;
    }
  • Button.h
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    #include <string>
    #include "Widget.h"

    class ButtonPrivate;
    class Button : public Widget
    {
    public:
    Button();
    ~Button();
    std::string text();
    void setText(std::string t);
    void paint();
    private:
    ButtonPrivate* d_ptr;
    };
  • Button.cpp
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    #include "Button.h"
    #include "ButtonPrivate.h"
    #include <iostream>

    Button::Button() : d_ptr(new ButtonPrivate)
    {
    d_ptr->q_ptr = this;
    std::cout << "Button is created." << std::endl;
    }
    Button::~Button()
    {
    if (d_ptr) { delete d_ptr; }
    std::cout << "Button is destroyed." << std::endl;
    }
    std::string Button::text() { return d_ptr->text; }
    void Button::setText(std::string t) { d_ptr->text = t; }
    // paint() 调用 公共类的方法
    void Button::paint() { d_ptr->q_ptr->update(); }
  • ButtonPrivate.h
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    #include <string>

    class Button;
    class ButtonPrivate
    {
    public:
    ButtonPrivate();
    ~ButtonPrivate();
    public:
    Button* q_ptr;
    std::string text;
    };
  • ButtonPrivate.cpp
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    #include "ButtonPrivate.h"
    #include "Button.h"
    #include <iostream>

    ButtonPrivate::ButtonPrivate()
    {
    std::cout << "ButtonPrivate is created." << std::endl;
    }
    ButtonPrivate::~ButtonPrivate()
    {
    std::cout << "ButtonPrivate is destroyed." << std::endl;
    }
  • main.cpp
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    #include <iostream>
    #include "Button.h"

    int main()
    {
    Button btn;
    btn.setText("世界和平");
    btn->paint();
    std::cout << "****************************" << std::endl;
    std::cout << "btn.width: " << btn.width() << std::endl;
    std::cout << "btn.height: " << btn.height() << std::endl;
    std::cout << "btn.text: " << btn.text() << std::endl;
    std::cout << "****************************" << std::endl;
    return 0;
    }
  • 控制台输出
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    WidgetPrivate is created.
    Widget is created.
    ButtonPrivate is created.
    Button is created.
    Widget::update() is running.
    ****************************
    btn.width: 100
    btn.height: 40
    btn.text: 世界和平
    ****************************
    ButtonPrivate is destroyed.
    Button is destroyed.
    WidgetPrivate is destroyed.
    Widget is destroyed.

另外一种构造直接传参赋值给q_ptr的写法:

1
2
3
4
// WidgetPrivate:添加一个公有对象的参数
WidgetPrivate(Widget* w) : q_ptr(w) {}
// Widget:创建一个d_ptr,并赋值d_ptr的q_ptr为this
Widget() : d_ptr(new WidgetPrivate(this)) {}

使用继承来优化d-pointer

由于每一个公有类都存在一个指向私有类对象的指针,因此创建公有类对象的同时就会创建私有类对象。上述例子中,创建了一个Button对象,会同时创建ButtonPrivateWidgetPrivate,如果继承层级多的情况下,将会自下往上创建私有类的内存分配。
为了解决这个问题,我们可以:保留基类的d_ptr,去除子类的d_ptr,并让子公共类自下而上传递d_ptr,这样做的前提是私有类必须有继承关系。

  • 私有类需要具有多层继承关系;
  • 去掉子公共类的d_ptr,只保留基公共类的d_ptr;
  • 去掉子私有类的q_ptr,只保留基私有类的q_ptr;
  • 所有公共类加入protected权限的构造函数,允许子公共类的构造函数传递子私有类对象来初始化父公共类的d_ptr,将该d_ptr一层层传递到最顶层。
1
2
3
4
5
6
7
8
9
10
11
class Widget;
class WidgetPrivate
{
public:
WidgetPrivate();
virtual ~WidgetPrivate();
public:
Widget* q_ptr;
int width;
int height;
};
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
#include "WidgetPrivate.h"
#include "Widget.h"
#include <iostream>

WidgetPrivate::WidgetPrivate(): width(100), height(40)
{
std::cout << this << "--WidgetPrivate is created." << std::endl;
}
WidgetPrivate::~WidgetPrivate()
{
std::cout << this << "--WidgetPrivate is destroyed." << std::endl;
}```

```c++
#include <string>
#include "WidgetPrivate.h"

class Button;
class ButtonPrivate : public WidgetPrivate
{
public:
ButtonPrivate();
virtual ~ButtonPrivate();
public:
std::string text;
};
1
2
3
4
5
6
7
8
9
10
11
12
#include "ButtonPrivate.h"
#include "Button.h"
#include <iostream>

ButtonPrivate::ButtonPrivate()
{
std::cout << this << "--ButtonPrivate is created." << std::endl;
}
ButtonPrivate::~ButtonPrivate()
{
std::cout << this << "--ButtonPrivate is destroyed." << std::endl;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class WidgetPrivate;
class Widget
{
public:
Widget();
~Widget();
int width();
void setWidth(int w);
int height();
void setHeight(int h);
void update();
protected:
Widget(WidgetPrivate &d);
WidgetPrivate* 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
#include "Widget.h"
#include <iostream>
#include "WidgetPrivate.h"

Widget::Widget() : d_ptr(new WidgetPrivate)
{
d_ptr->q_ptr = this;
std::cout << this << "--Widget 1 is created." << std::endl;
}

Widget::Widget(WidgetPrivate &d) : d_ptr(&d)
{
std::cout << this << "--Widget 2 is created." << std::endl;
}
Widget::~Widget()
{
if (d_ptr) { delete d_ptr; }
std::cout << this << "--Widget is destroyed." << std::endl;
}
int Widget::width() { return d_ptr->width; }
void Widget::setWidth(int w) { d_ptr->width = w; }
int Widget::height(){ return d_ptr->height; }
void Widget::setHeight(int h) { d_ptr->height = h; }
void Widget::update()
{
std::cout << "Widget::update() is running." << std::endl;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#include <string>
#include "Widget.h"

class ButtonPrivate;
class Button : public Widget
{
public:
Button();
~Button();
std::string text();
void setText(std::string t);
void paint();
protected:
Button(ButtonPrivate &d);
};
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
#include "Button.h"
#include "ButtonPrivate.h"
#include <iostream>

Button::Button() : Widget(*new ButtonPrivate)
{
std::cout << this << "--Button 1 is created." << std::endl;
}

Button::Button(ButtonPrivate &d) : Widget(d)
{
std::cout << this << "--Button 2 is created." << std::endl;
}
Button::~Button()
{
if (d_ptr) { delete d_ptr; }
std::cout << this << "--Button is destroyed." << std::endl;
}
std::string Button::text() {
ButtonPrivate *d = reinterpret_cast<ButtonPrivate*>(d_ptr);
return d->text;
}
void Button::setText(std::string t) {
ButtonPrivate *d = reinterpret_cast<ButtonPrivate*>(d_ptr);
d->text = t;
}
// paint() 调用 公共类的方法
void Button::paint() { d_ptr->q_ptr->update(); }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#include <iostream>
#include "Button.h"

int main()
{
Button btn;
btn.setText("世界和平");
btn.paint();
std::cout << "****************************" << std::endl;
std::cout << "btn.width: " << btn.width() << std::endl;
std::cout << "btn.height: " << btn.height() << std::endl;
std::cout << "btn.text: " << btn.text() << std::endl;
std::cout << "****************************" << std::endl;
return 0;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
0x807700--WidgetPrivate is created.
0x807700--ButtonPrivate is created.
0x60fe94--Widget 2 is created.
0x60fe94--Button 1 is created.
Widget::update() is running.
****************************
btn.width: 100
btn.height: 40
btn.text: 世界和平
****************************
0x807700--ButtonPrivate is destroyed.
0x807700--WidgetPrivate is destroyed.
0x60fe94--Button is destroyed.

使用宏来优化d-pointer和q-pointer

在上面的例子中,d_ptr是基公共类的指针,因此子公共类想要使用d_ptr获取独有的成员变量时,就需要强转为适当的类型。

1
ButtonPrivate *d = reinterpret_cast<ButtonPrivate*>(d_ptr);

但是,这么长一串并不美观而且重复。
因此,我们可以定义两个宏来代替强转语句。新建global.h,写入以下语句:

1
2
3
4
// 在公共类中获取指向私有类的指针。使用时,您只需在公共类的方法中调用 W_D(Class),它将在该位置定义一个指向类 Class 私有实现的指针 d
#define W_D(Class) Class##Private* d = reinterpret_cast<Class##Private*>(d_ptr);
// 在私有类中获取指向公共类的指针
#define W_Q(Class) Class* q = reinterpret_cast<Class*>(d_ptr);

Button.cpp的代码就可以改成:

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
#include "Button.h"
#include "ButtonPrivate.h"
#include <iostream>
#include <QObject>

Button::Button() : Widget(*new ButtonPrivate)
{
std::cout << this << "--Button 1 is created." << std::endl;
}

Button::Button(ButtonPrivate &d) : Widget(d)
{
std::cout << this << "--Button 2 is created." << std::endl;
}
Button::~Button()
{
std::cout << this << "--Button is destroyed." << std::endl;
}
std::string Button::text() {
W_D(Button)
return d->text;
}
void Button::setText(std::string t) {
W_D(Button)
d->text = t;
}
// paint() 调用 公共类的方法
void Button::paint() { d_ptr->q_ptr->update(); }

【TODO】Qt中的d-pointer

在Qt中,绝大部分公共类都使用d指针方案。
唯一一种特殊情况是,事先知晓类永远不会添加额外的成员变量,才会将数据成员直接存储在类本身,如Qpoint,QRect类。
在Qt中,所有私有对象的基类都是QObjectPrivate

Qt也是在src/corelib/global/qglobal.h中定义了两个宏,使得强转书写更简单;

宏:Q_D(Class)

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

d_func() 是内联函数,它返回指向 ClassPrivate 的指针;

宏:W_DECLARE_PRIVATE(Class)

1
2
3
4
5
6
#define W_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;

宏 W_DECLARE_PRIVATE 会在类的定义中声明 d_func() 方法,以便返回一个指向私有类 ClassPrivate 的指针;
通过使用 reinterpret_cast 对 d_ptr 进行类型转换,并调用 qGetPtrHelper 函数来处理指针的获取。
friend class Class##Private; 声明使得 ClassPrivate 可以访问 Class 的私有成员。

函数:qGetPtrHelper()

1
2
3
4
// 接受一个指向对象的指针并直接返回它
template <typename T> inline T *qGetPtrHelper(T *ptr) { return ptr; }
// 接受一个对象的引用,如果该对象重载了 operator->(),则返回该对象的指针。这使得它可以处理智能指针或其他类的对象。
template <typename Ptr> inline auto qGetPtrHelper(Ptr &ptr) -> decltype(ptr.operator->()) { return ptr.operator->(); }

qGetPtrHelper 函数是一个辅助模板函数,目的是提供一种安全的方式来获取指向对象的指针。

宏:Q_Q(Class)

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

q_func() 是内联函数,它返回指向 Class 的指针;

宏:Q_DECLARE_PUBLIC(Class)

用于在私有类中访问其公共类的指针。它的作用是简化在私有实现中获取公共接口的指针。可以让私有实现类(例如 ClassPrivate)在需要时方便地访问其对应公共类(例如 Class)的功能。它通常与 Q_Q(Class) 宏一起使用,后者用于在公共接口中获取指向私有实现的指针。

1
2
3
4
#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;

global.h

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#ifndef GLOBAL_H
#define GLOBAL_H

template <typename T> inline T *qGetPtrHelper(T *ptr) { return ptr; }
template <typename Ptr> inline auto qGetPtrHelper(Ptr &ptr) -> decltype(ptr.operator->()) { return ptr.operator->(); }

#define W_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;

#define W_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;

#define W_D(Class) Class##Private * const d = d_func()
#define W_Q(Class) Class * const q = q_func()

#endif // GLOBAL_H
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#include <string>
#include "Widget.h"

class ButtonPrivate;
class Button : public Widget
{
public:
Button();
~Button();
std::string text();
void setText(std::string t);
void paint();
protected:
Button(ButtonPrivate &d);
W_DECLARE_PRIVATE(Button)
};
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
#include "Button.h"
#include "ButtonPrivate.h"
#include <iostream>

Button::Button() : Widget(*new ButtonPrivate)
{
std::cout << this << "--Button 1 is created." << std::endl;
}

Button::Button(ButtonPrivate &d) : Widget(d)
{
std::cout << this << "--Button 2 is created." << std::endl;
}
Button::~Button()
{
if (d_ptr) { delete d_ptr; }
std::cout << this << "--Button is destroyed." << std::endl;
}
std::string Button::text() {
W_D(Button);
return d->text;
}
void Button::setText(std::string t) {
W_D(Button);
d->text = t;
}
// paint() 调用 公共类的方法
void Button::paint() { d_ptr->q_ptr->update(); }