d指针在Qt上的应用及实现
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所指向的堆内存。
d指针在Qt上的应用及实现的更多相关文章
- d指针在Qt上的应用及实现(d指针能实现二进制兼容)
Qt为了使其动态库最大程度上实现二进制兼容,引入了d指针的概念.那么为什么d指针能实现二进制兼容呢?为了回答这个问题,首先弄清楚什么是二进制兼容?所谓二进制兼容动态库,指的是一个在老版本库下运行的程序 ...
- d指针在Qt上的应用及实现(有图,很清楚)
Qt为了使其动态库最大程度上实现二进制兼容,引入了d指针的概念.那么为什么d指针能实现二进制兼容呢?为了回答这个问题,首先弄清楚什么是二进制兼容?所谓二进制兼容动态库,指的是一个在老版本库下运行的程序 ...
- Qt上FFTW組件的编译与安裝
Qt上FFTW組件的編譯安裝 FFTW是一個做頻譜非常實用的組件,本文講述在Windows和Linux兩個平臺使用FFTW組件.Windows下的的FFTW組件已經編譯好成爲dll文件,按照開發應用的 ...
- Xenko基础API笔记3- Pointers指针设备屏幕上点对应的手指触摸。
样本这里是一个简单的示例程序,跟踪目前在屏幕上的指针和打印他们的位置.访问输入字段,类继承自@ SiliconStudio.Xenko.脚本的类. public override async Task ...
- qt上用opencv显示摄像头视频
参考:http://blog.csdn.net/augusdi/article/details/8865541 代码如下: 注意,要在ui界面上放置一个“Vertical Layout”控件,调整到合 ...
- 智能指针剖析(上)std::auto_ptr与boost::scoped_ptr
1. 引入 C++语言中的动态内存分配没有自动回收机制,动态开辟的空间需要用户自己来维护,在出函数作用域或者程序正常退出前必须释放掉. 即程序员每次 new 出来的内存都要手动 delete,否则会造 ...
- C++智能指针剖析(上)std::auto_ptr与boost::scoped_ptr
1. 引入 C++语言中的动态内存分配没有自动回收机制,动态开辟的空间需要用户自己来维护,在出函数作用域或者程序正常退出前必须释放掉. 即程序员每次 new 出来的内存都要手动 delete,否则会造 ...
- seek指针大文件上传
package mainimport ( // "bufio" "fmt" "github.com/axgle/mahonia&qu ...
- [转]QT中的D指针与Q指针
Qt为了使其动态库最大程度上实现二进制兼容,引入了d指针的概念. 那么为什么d指针能实现二进制兼容呢? 为了回答这个问题,首先弄清楚什么是二进制兼容? 所谓二进制兼容动态库,指的是一个在老版本库下运行 ...
随机推荐
- c# 替换非法字符
保存文件的时候,文件名不允许非法字符. public string ReplaceSpecialCharacter(string str) { List<ch ...
- select查询原理
原文:select查询原理 我并非专业DBA,但做为B/S架构的开发人员,总是离不开数据库,一般开发员只会应用SQL的四条经典语句:select ,insert,delete,update.但是我从来 ...
- SOD框架的数据容器,打造最适合DDD的ORM框架
SOD框架的数据容器,打造最适合DDD的ORM框架 引言:DDD的困惑 最近,我看到园子里面有位朋友的一篇博客 <领域驱动设计系列(一):为何要领域驱动设计? >文章中有下面一段话,对DD ...
- 【iOS】Xib的使用与File'Owner总结
一.XIB的适用范围 xib(也叫Nib)与storyboard一样是用来描写叙述界面的. storyboard描写叙述的是比較大型的,大范围.适合描写叙述界面跳转等. 二.XIB的使用 Xib是小范 ...
- 完整的thinphp+phpexcel实现excel报表的输出(有图有效果)
准备工作:1.下载phpexcel1.7.6类包:2.解压至TP框架的ThinkPHP\Vendor目录下,改类包文件夹名为PHPExcel176,目录结构如下图: 编写代码(以一个订单汇 ...
- ios背景更新和下载
ios背景更新和下载 by 吴雪莹 - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NS ...
- webBrowser 参数设置
//禁用脚本错误等类似的窗口信息 this.webBrowser1.ScriptErrorsSuppressed = true; //禁用右键菜单 this.webBrowser1.IsWebBrow ...
- 探秘IntelliJ IDEA 13测试版新功能——调试器显示本地变量
IntelliJ IDEA在业界被公认为最好的Java开发平台之一,JetBrains公司将在12月正式发布IntelliJ IDEA 13版本. 现在,小编将和大家一起探秘密IntelliJ IDE ...
- Mac OSX操作系统安装和配置Zend Server 6教程(1)
作为web开发人员,应该熟悉掌握各种系统下安装和配置web服务器与站点的技术. 随着越来越多的开发人员选择Zend Server服务器,慧都推出了在Mac OSX系统安装和配置Zend Server ...
- 关于knob.js进度插件的使用
关于这个插件,妹的,第一次使用坑死爹了,各种不会,幸亏我有持之以恒的精神,最终还是让其臣服于我的胯下.... 1. 引入 head 部分添加knob.js,同时引入excanvas.js这个文件主 ...