_ZTS7QObject

一、Q_D的在文件中的提法

  Q_D的设置意在方便地获取私有类指针,文件为qglobal.h。下面的##是宏定义的连字符。假设类名是A,那么A##Private翻译过来就是APrivate。

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

  d_func()函数如下实现:

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

  这里的d_func()虽然在宏里面,但是如果代入具体的类型,这里面就变成了以Class##Private的私有类指针为返回值,以func为函数名的函数。这里的qGetPtrHelper是

 template <typename T> static inline T *qGetPtrHelper(T *ptr) { return ptr; }

  这个模板函数里面里的T要套入某个具体的类。上面的那个Q_DECLARE_PRIVATE里面的就成了这个模板类的一个调用。这个模板类就是把这个类指针转换成了静态的。有了上面的这三段代码,如果想在某个类A里面声明一个私有类,直接来一个Q_D(A),再Q_DECLARE_PRIVATE(A)就可以了。

二、宏和模板的展开

  展开前一个宏和后一个宏的一部分,成了

 #define Q_D(A) APrivate *const d= d_func()
 inline APrivate* d_func() { return reinterpret_cast<APrivate *>(qGetPtrHelper((d_ptr));}

  上面这个函数qGetPtrHelper调用的输入值是QObject类里面的一个成员变量,d_ptr指针,定义如下:

     QScopedPointer<QObjectData> d_ptr;

  根据模板函数调用返回的仍然是一个QScopedPointer<QObjectData>类型的变量,也就是一个指向QObjectData类型的智能限域指针。最后调用reinterpret_cast重新解释前面得到的指针,把它变成指向APrivate类型的。

  这里的QObjectData的定义也在QObject里面

 class Q_CORE_EXPORT QObjectData {
public:
virtual ~QObjectData() = ;
QObject *q_ptr;
QObject *parent;
QObjectList children; uint isWidget : ;
uint blockSig : ;
uint wasDeleted : ;
uint isDeletingChildren : ;
uint sendChildEvents : ;
uint receiveChildEvents : ;
uint isWindow : ; //for QWindow
uint unused : ;
int postedEvents;
QDynamicMetaObjectData *metaObject;
QMetaObject *dynamicMetaObject() const;
};

  至于QScopedPointer,有点复杂,核心思想是一个不需要自己销毁的指针。

三、继承和调用

  自己写一个类MyQFileSystemModel继承QFileSystemModel,在MyQFileSystemModel中使用Q_D宏,会出现错误:

 C:\Qt\Qt5.9.2\5.9.\mingw53_32\include\QtWidgets/qfilesystemmodel.h: In constructor 'MyQFileSystemModel::MyQFileSystemModel()':
C:\Qt\Qt5.9.2\5.9.\mingw53_32\include/QtCore/qglobal.h::: error: 'QFileSystemModelPrivate* QFileSystemModel::d_func()' is private
inline Class##Private* d_func() { return reinterpret_cast<Class##Private *>(qGetPtrHelper(d_ptr)); } \
^
C:\Qt\Qt5.9.2\5.9.\mingw53_32\include\QtWidgets/qfilesystemmodel.h::: note: in expansion of macro 'Q_DECLARE_PRIVATE'
Q_DECLARE_PRIVATE(QFileSystemModel)
^
C:\Qt\Qt5.9.2\5.9.\mingw53_32\include/QtCore/qglobal.h::: error: within this context
#define Q_D(Class) Class##Private * const d = d_func()
^
..\student\myqfilesystemmodel.cpp::: note: in expansion of macro 'Q_D'
Q_D(const QFileSystemModel);
^
C:\Qt\Qt5.9.2\5.9.\mingw53_32\include/QtCore/qglobal.h::: warning: unused variable 'd' [-Wunused-variable]
#define Q_D(Class) Class##Private * const d = d_func()
^
..\student\myqfilesystemmodel.cpp::: note: in expansion of macro 'Q_D'

  意思是,d_func()是QFileSystemModel这个类里面的私有函数,对于c++的私有函数,子类是不能够继承的。

四、q指针

  写一个MyQFileSystemModel的私有类MyQFileSystemModelPrivate,就可以使用Q_Q宏,从私有类引用对应的公有类了。

 #ifndef MYQFILESYSTEMMODEL_P_H
#define MYQFILESYSTEMMODEL_P_H
#include <myqfilesystemmodel.h>
class MyQFileSystemModelPrivate;
class MyQFileSystemModel;
QT_BEGIN_NAMESPACE
class MyQFileSystemModelPrivate: public QFileSystemModelPrivate
{
public:
//MyQFileSystemModelPrivate(MyQFileSystemModel * parent):q_ptr(parent){}
public:
Q_DECLARE_PUBLIC(MyQFileSystemModel)
MyQFileSystemModel *q_ptr;
};
QT_END_NAMESPACE
#endif // MYQFILESYSTEMMODEL_P_H

  这里面需要尤其注意的是,MyQFileSystemModel *q_ptr;这一行是不能少的。c++的static_cast是对被转换的类型有限制的。如果B类继承了A类,那么从B类转换成A类是完全没有问题的。但是,如果想把A类转换成B类,就要求B类里面,问题就出现了,A类可能没有B类那么丰满,转换出来的类可能是残废的,所以一般情况下,这种转换是不能成功的。除非:B类里面包含了一个指向A类的指针。如果把q_ptr指针声明去掉了,会报这个错误:

 C:\Qt\Qt5.9.2\5.9.\mingw53_32\include\QtCore\qglobal.h:: error: invalid static_cast from type 'QObject*' to type 'MyQFileSystemModel*'
inline Class* q_func() { return static_cast<Class *>(q_ptr); } \
^

五、私有类函数调用

  Qt中的公有类和私有类关系密切,私有类的函数是不能够直接使用的。要是想用,一定要通过其他类调用。在Qt编译的时候,QFileSystemModel.h是一堆声明,会导出很多函数到QtWidget.dll里面去。值得注意的是,这里导出的只是这个文件里所声明的类、函数和变量,而不会导出只在私有类头文件QFileSystemModel_p.h里面声明过的函数和变量。

  当然,编译的时候,也会引用QFileSystemModelPrivate.h和QFileSystemModel.cpp里的声明以及定义。一般情况下,我们只是Qt的使用者,在windows系统下,只要下载安装就好,不需要自己再编译了。但是当我们想使用私有类做一些更深入的定制的时候,希望能够调直接调用私有类的成员。这时候,如果只是include了.h文件,就会报undefined reference错误。也就是说,编译能过,链接过不了,找不到私有类成员。

  解决的方法就是将对应的cpp文件也包含进工程目录里面,但这时候会有新的问题出现。有些类的实现已经编译到库函数里面了,这时候又在cpp文件重新实现,会报警告:redeclared without dllimport attribute。只需要把cpp文件中的实现函数删除掉就可以了。

  虽然通过上面的方法,可以实现私有类的使用,但是值得注意的是,已经编译好的二进制文件dll里面的同名私有函数还在起着作用。还是用QFileSystemModel为例,它已经被Qt编译好放到了QWidget.dll里面了。在此dll文件里也会有QFileSystemModelPrivate的函数(只是不会有直接指向外部的声明,外部不能直接链接引用而已),否则私有类就完全没用了。当新的工程要使用到QFileSystemModel,而QFileSystemModelPrivate被间接地引用,使用的版本就是之前编译好的成品。当新的工程要直接调用私有类函数的时候,才是用的新的版本。

  用Dependency打开Qt5Widgets.dll看到私有类的函数名形式如下:

 _ZN16QFileSystemModelC1ER23QFileSystemModelPrivateP7QObject

  这可能只是一个构造函数,在Qt5Widgets.dll里只有两个上面这种QFileSystemModelPrivate的函数,可见大多数私有类的函数没有导出。而直接用本节方法编译出来的QFileSystemModelPrivate.dll里面的私有成员函数形式如下:  

 _ZNK23QFileSystemModelPrivate4nodeERK11QModelIndex

  代表形参是QModelIndex的私有类函数node,像这样的函数还有很多,它们与私有类的.h文件能够对应起来。

Qt中的Q_D宏和d指针的更多相关文章

  1. Qt中的Q_PROPERTY宏浅析

    1. Q_PROPERTY Qt提供了一个绝妙的属性系统,Q_PROPERTY()是一个宏,用来在一个类中声明一个属性property,由于该宏是qt特有的,需要用moc进行编译,故必须继承于QObj ...

  2. Qt中无处不在的d指针为何方神圣

    在研究QCoreApplication类的代码时,无意间弄明白了“d_func()”和“d指针”的来源: class Q_CORE_EXPORT QCoreApplication#ifndef QT_ ...

  3. [转]QT中的D指针与Q指针

    Qt为了使其动态库最大程度上实现二进制兼容,引入了d指针的概念. 那么为什么d指针能实现二进制兼容呢? 为了回答这个问题,首先弄清楚什么是二进制兼容? 所谓二进制兼容动态库,指的是一个在老版本库下运行 ...

  4. cocos2dx中CC_CALLBACK_1等宏中this指针实际指向

    首先看代码,我在Helloworld中添加两个函数. void HelloWorld::addTarget(){ Size visibleSize = Director::getInstance()- ...

  5. Qt中的ui指针和this指针

    初学qt,对其ui指针和this指针产生疑问,画了个把小时终于搞懂了. 首先看ui指针的定义: 在mainwindow.h中 private: Ui::MainWindow *ui; Ui又是什么? ...

  6. 智能指针类模板(中)——Qt中的智能指针

    Qt中的智能指针-QPointer .当其指向的对象被销毁时,它会被自动置空 .析构时不会自动销毁所指向的对象-QSharedPointer .引用计数型智能指针 .可以被自由的拷贝和赋值 .当引用计 ...

  7. PyQt(Python+Qt)学习随笔:Qt中的部分类型QString、QList和指针、引用在PyQt中的实现方式

    老猿Python博文目录 老猿Python博客地址 在我们查阅Qt的文档资料时,可以看到Qt中的链表使用的是QList,字符串使用的是QString,但老猿在测试时发现这两个类型PyQt不支持,无法找 ...

  8. Q_D宏

    Qt 源码中有很多Q_Q和Q_D宏,使用这些宏的地方总会看到有q指针和d指针,查了查KDE文档,大体搞清了其中的机理,欧也!Qt的这些私有数据访问策略还是挺值得借鉴.下面就简单总结一下. 访问器 , ...

  9. Qt中单例模式的实现(4种方法)

    最简单的写法: 12345 static MyClass* MyClass::Instance(){ static MyClass inst; return &inst;} 过去很长一段时间一 ...

随机推荐

  1. kafka消费者实时消费数据存入hdfs java scalca 代码

    hadoop-client依赖很乱 调试很多次cdh版本好多jar没有 用hadoop2.7.3可以 自定义输出流的池子进行流管理 public void writeLog2HDFS(String p ...

  2. Docker Swarm 环境搭建

    Docker Swarm 环境搭建 swarm 使用前提 1.Docker版本1.12+ Docker升级教程:https://www.cnblogs.com/xiangsikai/p/9935894 ...

  3. [Python]基础教程(2)、PyCharm安装及中文编码

    一.PyCharm安装 http://blog.csdn.net/yctjin/article/details/70307933?locationNum=11&fps=1 这篇文章写得及其详细 ...

  4. bzoj1001狼抓兔子 对偶图优化

    bzoj1001狼抓兔子 对偶图优化 链接 https://www.lydsy.com/JudgeOnline/problem.php?id=1001 思路 菜鸡总是要填坑的! 很明显让你求网格图的最 ...

  5. 用bytomswap进行“跨链”资产转换

    bytom是专注资产领域的公有区块链平台,最近开发者社区基于比原做了一款资产转换平台.我们可以在上面通过自己现有的资产在比原上发行资产.然后达到资产转换的目的. 一. 以太币资产转换成比原上的资产 首 ...

  6. 友盟分享因为Bundle Id 校验不通过 无法分享到微信

    微信分享应用里面资料有个APP bundle id需要填的, 以前申请的时候不需要填也可以正常分享, 但是最近开始微信需要验证, 在那填上APP对应bundle ID 就可以了

  7. Windows10家庭版用户无法在计算机管理更改权限的解决办法

    问题描述:今天因为动了注册表导致windows登陆界面只剩下一个管理员账号,而平常用的账号不知所踪~这个问题本来很好解决,但是由于在用的笔记本安装的是Windows家庭中文版,无法通过计算机管理在本地 ...

  8. sublime text 3 显示空格和Tab

    tab对于需要 共享 的代码而言是灾难. pyhton对这点的明确要求是不要混合使用4个空格和tab键. 只使用二者之一,都是没有问题的.如果一个python代码已经完成的部分,都是使用的4个空格.那 ...

  9. minicom 安装 查看串口

    因为现在电脑基本不配备串行接口,所以,usb转串口成为硬件调试时的必然选择.目前知道的,PL2303的驱动是有的,在dev下的名称是ttyUSB0 默认情况下ubuntu已经安装了USB转串口驱动(p ...

  10. 环境准备—之—linux下安装jdk

    本文大部分转自  https://www.cnblogs.com/Dylansuns/p/6974272.html openjdk与jdk的区别 ----反正不一样,要不名字也不会不相同了 https ...