Qt为了使其动态库最大程度上实现二进制兼容,引入了d指针的概念。数据结构
那么为何d指针能实现二进制兼容呢?函数
为了回答这个问题,首先弄清楚什么是二进制兼容?ui
所谓二进制兼容动态库,指的是一个在老版本库下运行的程序,在不通过编译的状况下,仍然可以在新的版本库下运行;须要通过编译才能在新版本下运行,而不须要修改该程序源代码,咱们就说该动态库是源代码兼容的。要使一个dll能达到二进制兼容,对于一个结构,对于一个对象,其数据模型应该不变,如有变更,好比在类中增长数据成员或删除数据成员,其结果确定影响对象的数据模型,从而致使原有数据程员在对象数据模型里的位移发生变化,这样的话编译后的新版本库极可能使程序发生崩溃,为了使在增长和添加项后不使对象数据模型大小发生变化,一种作法是预先分配若干个保留空间,当要添加项时,使用保留项。以下:this
class A {
private:
int a;
int reserved[3];
};spa
class B {
private:
int a;
quint32 b : 1;
quint32 reserved : 31;
};翻译
这样的话,当增长项的时候,只须要利用reserved空间(组态王中好多数据结构就有保留字段,如变量的域),这样的话,对象模型就不会改变。可是这种作法很呆板,由于你不知道将来到底会有多少扩展项,少了不知足要求,多了浪费空间。那么有没有一种更灵活的方法呢?以下:指针
class Data {
public:
int a;
};对象
class A {
private:
Data *d_ptr;
};blog
将A中的成员a放入Data 中,A中放入Data的一个指针,这样的话,不管你向Data中添加多少数据,A的对象模型始终是4个字节的大小(d_ptr指针的大小),这种作法是否是比上面的作法更灵活呢?d_ptr就是咱们今天所要说的d指针,Qt为了实现二进制兼容,绝大数类中都包含有这样的指针,下面咱们一块儿来看看Qt的d指针是怎么实现的:继承
如上图,这个是Qt根结点的指针的通常形式,下面来看看非根结点的通常形式,
注意这里QWidget派生自QObject,它里面没有d_ptr,可是它的成员函数能够访问d_ptr,由于 d_ptr是保护成员,且它的对象模型包含 d_ptr(这是由于派生类继承父类的全部成员)。
下面咱们来看看Qt对上述两种状况是怎么实现的:
qobject.h文件:
QObjectData {
public:
QObject *q_ptr;
...
};
class Q_CORE_EXPORT QObject
{
...
Q_DECLARE_PRIVATE(QObject)
public:
Q_INVOKABLE explicit QObject(QObject *parent=0);
virtual ~QObject();
...
protected:
QObject(QObjectPrivate &dd, QObject *parent = 0);
...
protected:
QScopedPointer<QObjectData> d_ptr;
...
};
如上,在这里我省去了其余的项,只保留了于d_ptr有关的项,首先来看看Q_DECLARE_PRIVATE(QObject)是什么:
#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;
根据宏定义,则Q_DECLARE_PRIVATE(QObject)翻译以下:
inline QObjectPrivate *d_func()
{
return reinterpret_cast<QObjectPrivate *>(qGetPtrHelper(d_ptr));
}
inline const QObjectPrivate *d_func() const
{
return reinterpret_cast<const QObjectPrivate *>(qGetPtrHelper(d_ptr));
}
friend class QObjectPrivate;
再来看看qGetPtrHelper的定义:
template <typename T> static inline T *qGetPtrHelper(T *ptr)
{
return ptr;
}
再来看QScopePointer,它相似于智能指针,这样不用关心 d_ptr的释放,当离开QScopePointer的做用范围,QScopePointer会自动释放d_ptr指向的堆内存,那么这个指针是何时生成的呢?q_ptr又是何时赋值的呢?让咱们来看看qobject.cpp的实现:
QObject::QObject(QObject *parent)
: d_ptr(new QObjectPrivate)
{
Q_D(QObject);
d_ptr->q_ptr = this;
...
}
QObject::QObject(QObjectPrivate &dd, QObject *parent)
: d_ptr(&dd)
{
Q_D(QObject);
d_ptr->q_ptr = this;
...
}
咱们看第一个构造函数,对于根结点的d_ptr指向new QObjectPrivate,而QObjectPrivate派生自QObjectData,那么Q_D(QObject)宏表示什么意思呢?
#define Q_D(Class) Class##Private * const d = d_func()
Q_D(QObject);翻译以下:
QObjectPrivate * const d = d_func();
不难看出Q_D(QObject);定义了一个QObjectPrivate的常量指针,指向d_func() 的返回值,而该返回值,正是d_ptr(见头文件 d_func()的定义),所以经过Q_D宏咱们就能够访问d指针了。
对于第二个构造函数稍后介绍,下面来看看非根结点的d_ptr的实现状况:
头文件:
class Q_CORE_EXPORT QObjectPrivate : public QObjectData
{
Q_DECLARE_PUBLIC(QObject)
...
};
class Q_GUI_EXPORT QWidgetPrivate : public QObjectPrivate
{
Q_DECLARE_PUBLIC(QWidget)
...
};
class Q_GUI_EXPORT QWidget : public QObject
{
...
Q_DECLARE_PRIVATE(QWidget)
...
public:
...
explicit QWidget(QWidget* parent = 0, Qt::WindowFlags f = 0);
...
};
咱们首先来看看Q_DECLARE_PUBLIC宏:
#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(QObject)翻译以下:
template <typename T> static inline T *qGetPtrHelper(T *ptr)
{
return ptr;
}
inline QObject *q_func()
{
return static_cast<QObject *>(q_ptr);
}
inline const QObject *q_func() const
{
return static_cast<const QObject *>(q_ptr);
}
friend class QObject;
Q_DECLARE_PUBLIC(QWidget)翻译以下:
inline QWidget *q_func()
{
return static_cast<QWidget *>(q_ptr);
}
inline const QWidget *q_func() const
{
return static_cast<const QWidget *>(q_ptr);
}
friend class QWidget;
注意这里的q_ptr是在QObjectData里公有声明的,QObjectPrivate,QWidgetPrivate都派生或间接派生自QObjectData,因此能够访问q_ptr。
接下来看Q_DECLARE_PRIVATE(QWidget)的翻译:
inline QWidgetPrivate *d_func()
{
return reinterpret_cast<QWidgetPrivate *>(qGetPtrHelper(d_ptr));
}
inline const QWidgetPrivate *d_func() const
{
return reinterpret_cast<const QWidgetPrivate *>(qGetPtrHelper(d_ptr));
}
friend class QWidgetPrivate;
接下来看看QWidget的构造函数的实现:
QWidget::QWidget(QWidget *parent, Qt::WindowFlags f)
: QObject(*new QWidgetPrivate, 0)
{
...
}
看到QObject(*new QwidgetPrivate, 0)这里调用了QObject的第二个构造函数,将d_ptr指向new QWidgetPrivate所指向的堆内存。
还能够参考https://wiki.qt.io/D-Pointer/zh,这个讲的不错!