原文标题:d指针在Qt上的应用及实现

原文链接:http://blog.csdn.net/rabinsong/article/details/9474859

正文:

Qt为了使其动态库最大程度上实现二进制兼容,引入了d指针的概念。那么为什么d指针能实现二进制兼容呢?为了回答这个问题,首先弄清楚什么是二进制兼容?所谓二进制兼容动态库,指的是一个在老版本库下运行的程序,在不经过编译的情况下,仍然能够在新的版本库下运行;需要经过编译才能在新版本下运行,而不需要修改该程序源代码,我们就说该动态库是源代码兼容的。要使一个dll能达到二进制兼容,对于一个结构,对于一个对象,其数据模型应该不变,若有变动,比如在类中增加数据成员或删除数据成员,其结果肯定影响对象的数据模型,从而导致原有数据程员在对象数据模型里的位移发生变化,这样的话编译后的新版本库很可能使程序发生崩溃,为了使在增加和添加项后不使对象数据模型大小发生变化,一种做法是预先分配若干个保留空间,当要添加项时,使用保留项。如下:

class A {  private:  int a;  int reserved[3];
};  class B {
private:  int a;  quint32 b : 1;  quint32 reserved : 31;
}; 

这样的话,当样增加项的时候,只需要利用reserved空间,这样的话,对象模型就不会改变。但是这种做法很呆板,因为你不知道未来到底会有多少扩展项,少了不满足要求,多了浪费空间。那麽有没有一种更灵活的方法呢?如下:

class Data {
public:  int a;
};  class A {  private:  Data *d_ptr;
};  

将A中的成员a放入Data 中,A中放入Data的一个指针,这样的话,无论你向Data中添加多少数据,A的对象模型始终是4个字节的大小(d_ptr指针的大小),这种做法是不是比上面的做法更灵活呢?d_ptr就是我们今天所要说的d指针,Qt为了实现二进制兼容,绝大数类中都包含有这样的指针,下面我们一起来看看Qt的d指针是怎么实现的:

如上图,这个是Qt根结点的指针的一般形式,下面来看看非根结点的一般形式,

注意这里QWidge派生自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)翻译如下:

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所指向的堆内存。

Qt中Q_D宏及d指针相关推荐

  1. Qt中Q_D和Q_P的作用

    使用d和q指针是为了二进制兼容,更新dll动态库时,老版本不需要重新编译机可以正常运行. Q_D是宏定义,如下所示, #define Q_D(Class) Class##Private * const ...

  2. Qt中的d指针和q指针

    概述 如果程序从一个以前版本的库动态链接到新版本的库之后,能够继续正常运行,而不需要重新编译,那么我们就说这个库是二进制兼容的. 如果一个程序需要重新编译来运行一个新版本的库,但是不需要对程序的源代码 ...

  3. QT中的D指针与Q指针

    了解QT中引入D指针的初衷前需要了解下二进制兼容 1. 什么是二进制兼容 二进制兼容是针对动态链接库而言的,如果你的源代码已经编译成为可执行文件了,其中你调用了一些动态库,后来你为这个动态库进行了更新 ...

  4. 【QT小记】QT中智能指针的使用

    自定义两个类 class OnePiece { public:OnePiece() {qDebug() << QString("构造OP");}~OnePiece() ...

  5. 【Qt】Qt中信号与槽

    00. 目录 文章目录 00. 目录 01. 信号与槽 02. 介绍 03. 信号与槽 04. 信号 05. 槽 06. 一个小例子 07. 一个真实的例子 08. 信号和槽使用默认参数 09. 信号 ...

  6. Qt文档阅读笔记-Q_INVOKABLE官方解析及Qt中反射的使用

    Q_INVOKABLE宏定义了函数在元对象中可以进行调用,这个宏要写到返回值的前面,也就是函数的开头,如下例子: class Window : public QWidget{Q_OBJECTpubli ...

  7. 中的枚举属性函数_软件特攻队|Qt中的反射机制之Q_PROPERTY

    接触过Java的同学肯定知道"反射"这一神器,而所谓的反射,指的是对于任意的一个类,即使在运行状态下,都能知道这个类的所有方法和属性,并可以动态调用这些方法的一种机制. 但是非常遗 ...

  8. 【转】深度解析 Qt 中动态链接库

    转载:http://mobile.51cto.com/symbian-270525.htm 本文介绍的是Qt 中动态链接库,现在有些软件有自动升级功能,有些就是下载新的DLL文件,替换原来的动态链 接 ...

  9. Qt中的UI文件介绍

    UI文件是什么? .ui.ui.ui通常是指Qt设计师设计出来的界面文件的后缀,它本质上是一个标准XML格式的文本文件,需要通过uicuicuic工具将其转换为项目中可用的ui_∗.hui\_*.hu ...

最新文章

  1. C++~回溯+贪心法解决01背包问题
  2. 织梦根目录下面404页面,主页能正常运行404页面,切换至栏目页404页面内的图片不能正常显示,解决...
  3. 计算机网络-基本概念(3)【网络层】-路由选择协议
  4. 辨异 —— 不同的编程语言(编译型语言、解释型语言、动态语言、静态语言)...
  5. python安装依赖包经常出错怎么办_Python 下载依赖包环境经常失败超时解决方法...
  6. yii2 html转换,能够将yii2的api以html文件导出,以及相关的测试
  7. 使用Struts2框架开发租房系统(8)
  8. jsp分页带条件查询时,点击上一页或下一页,没有按条件查询数据。
  9. 产品经理常见沟通问题
  10. Java基础总结(上)
  11. 公众号滑动图代码_微信公众号的代码块插入,及一键排版
  12. 微信小程序:小程序开发中申请优惠券步骤
  13. Unity3D引擎各大插件免费下载地址
  14. 为大地增添一抹绿色植树节公益活动
  15. Ubuntu 20.04.1 安装ROS-noetic 遇到的环境设置问题
  16. 软件腐化的七个特征之牢固性和粘滞性(设计模式原则的反面) (《敏捷软件开发》读书总结第二篇)
  17. 国标GB/T28181EasyGBS对接大华硬盘录像机NVR查询录像失败解决方案
  18. 小米台灯突然自己亮了_升级版值不值得买?米家台灯1代1S对比测评
  19. GetLastError 错误码全解析
  20. JAVA移位运算符-左移、右移、无符号右移 (简解)

热门文章

  1. web-view和runtime.openURL的方式进行外部链接
  2. CSS中 定位position 和 transform 移动元素的比较
  3. 归一化php,归一化函数normalize详解
  4. 它又来了!FastJson又被发现漏洞,autotype的安全控制开关限制可以被绕过...你方了没?...
  5. Unity 调用摄像头拍照保存
  6. php字典遍历,OC中的遍历数组和字典
  7. 天边一朵云-最终章动画化决定,看云卷云舒
  8. 新浪随机图片壁纸API接口,刷新网页换背景接口
  9. BIM模型轻量化的方法总结
  10. BMS与充电机控制导引