[Switch] D pointer Q and the pointer in QT

Qt in order to make the maximum extent possible to achieve a dynamic library binary compatibility , introduced the concept of the pointer d.

So why d pointer to achieve binary compatible with it?

To answer this question, first figure out what is binary compatible?

The so-called binary compatibility DLL, referring to the program that runs under the old version of the library, is still able to run in the new repository without compiled; you need to run in the new version can be compiled without the need modify the source code, we say that the DLL source code compatible. To make a dll to achieve binary compatibility, for a structure for an object, the data model should be the same, subject to change, such as an increase in class data members or delete data members, and the results certainly affect the data model object, so leading to the displacement member of the original drive data in the object changes in the data model, so the compiled version of the new library is likely to make the program crash occurs, in order not to make the size of the object data model and add items to increase after a change in a practice are pre-assigned number of reserved space, when you want to add an item, use the reserved items. as follows:

class A {
private:
  int a;
  int reserved[3];
};

class B {
private:
  int a;
  quint32 b : 1;
  quint32 reserved : 31;
};

In this case, when adding items, you can just use reserved space (configuration king had retained a lot of the data structure fields, such as domain variables), this is the case, the object model will not change. But this approach is very dull, because in the end you do not know how many there will be future extensions, less does not meet the requirements, more than a waste of space. Is there a more flexible way to do that? as follows:

class Data {
public:
  int a;
};

class A {
private:
  Data *d_ptr;
};

A pointer to a member of a Load Data in the Data A placed, so, no matter how much data you add to the Data, the object model A is always 4 bytes in size (the size of d_ptr pointer) this approach is more flexible than the above is not a thing to do? d_ptr is what we have to say today d pointer, Qt order to achieve binary compatibility, the vast majority of the class all contain such a pointer , let's take a look at the d pointer Qt is how to achieve:

As shown above, this is the general form of the root pointer Qt, let's look at the non-root general form,

Note that QWidget derived from QObject, there is no d_ptr it, but it's member functions can access d_ptr, because d_ptr are protected members, and its object model contains d_ptr (This is because the derived class inherits all the members of the parent class).

 

Let us look at Qt for both cases is how to achieve:

qobject.h file:

 

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,这个讲的不错!

Guess you like

Origin www.cnblogs.com/MakeView660/p/11096195.html