前面我们已经看到了怎样使用标准的 C++ 代码以及 Qt 提供的 API 来达到信息隐藏这一目标。下面我们来看一下 Qt 是如何实现的。
还是以 QObject 的源代码作为例子。先打开 qobject.h,找到 QObjectData 这个类的声明。具体代码如下所示:
QObjectData {
public:
virtual ~QObjectData() = 0;
// others
};
然后在下面就可以找到 QObject 的声明:
class QObject
{
Q_DECLARE_PRIVATE(QObject)
public:
Q_INVOKABLE explicit QObject(QObject *parent=0);
protected:
QObject(QObjectPrivate &dd, QObject *parent = 0);
QScopedPointer<QObjectData> d_ptr;
// others
}
注意,这里我们只是列出了我们所需要的代码,并且我的 Qt 版本是 2010.03。这部分代码可能会随着不同的 Qt 版本所有不同。
首先先了解一下 Qt 的设计思路。既然每个类都应该把自己的数据放在一个 private 类中,那么,为什么不把这个操作放在几乎所有类的父类 QObject 中呢?所以,Qt 实际上是用了这样一个思路,实现了我们前面介绍的数据隐藏机制。
首先回忆一下,我们前面说的 D-Pointer 需要有一个 private 或者 protected 的指向自己数据类的指针。在 QObject 中,
QScopedPointer<QObjectData> d_ptr;
就扮演了这么一个角色。或许,你可以把它理解成
QObjectData *d_ptr;
这不就和我们前面说的 D-Pointer 技术差不多了?QScopedPointer 是 Qt 提供的一个辅助类,这个类保存有一个指针,它的行为类似于一种智能指针:它能够保证在这个作用域结束后,里面的所有指针都能够被自动 delete 掉。也就是说,它其实就是一个比普通指针更多功能的指针。而这个指针被声明成 protected 的,也就是只有它本身以及其子类才能够访问到它。这就提供了让子类不必须声明这个 D-Pointer 的可能。
那么,前面我们说,QObjectData 这种数据类不应该放在公开的头文件中,可 Qt 怎么把它放进来了呢?这样做的用途是,QObject 的子类的数据类都可能继承自这个 QObjectData。这个类有一个纯虚的析构函数。没有实现代码,保证了这个类不能被初始化;虚的析构函数,保证了其子类都能够被正确的析构。
回到我们前面说明的 Q_DECLARE_PRIVATE 这个宏:
#define Q_DECLARE_PRIVATE(Class) \
inline Class##Private* d_func() { return reinterpret_cast(qGetPtrHelper(d_ptr)); } \
inline const Class##Private* d_func() const { return reinterpret_cast(qGetPtrHelper(d_ptr)); } \
friend class Class##Private;
我们把代码中的 Q_DECLARE_PRIVATE(QObject) 展开看看是什么东西:
inline QObjectPrivate* d_func() { return reinterpret_cast(qGetPtrHelper(d_ptr)); }
inline const QObjectPrivate* d_func() const { return reinterpret_cast(qGetPtrHelper(d_ptr)); }
friend class QObjectPrivate;
清楚是清楚,只是这个 QObjectPrivate 是哪里来的?既然是 Private,那么它肯定不会在公开的头文件中。于是我们立刻想到到 qobject.cpp 或者是 qobject_p.h 中寻找。终于,我们在 qobject_p.h 中找到了这个类的声明:
class Q_CORE_EXPORT QObjectPrivate : public QObjectData
{
Q_DECLARE_PUBLIC(QObject)
public:
QObjectPrivate(int version = QObjectPrivateVersion);
virtual ~QObjectPrivate();
// others
}
这个类是继承 QObjectData 的!想想也是,因为我们说过,QObjectData 是不能被实例化的,如果要使用,必须创建它的一个子类。显然,QObjectPrivate 就扮演了这么一个角色了。不仅如此,我们还在这里看到了熟悉的 Q_DECLARE_PUBLIC 宏。好在我们已经知道它的含义了。
在 qobject.cpp 中,我们看一下 QObject 的构造函数:
QObject::QObject(QObject *parent)
: d_ptr(new QObjectPrivate)
{
// others
}
QObject::QObject(QObjectPrivate &dd, QObject *parent)
: d_ptr(&dd)
{
// others
}
第一个构造函数就是我们经常见到的那个。它使用自己创建的 QObjectPrivate 指针对 d_ptr 初始化。第二个构造函数使用传入的 QObjectPrivate 对象,但它是 protected 的,也就是说,你不能在外部类中使用这个构造函数。那么这个构造函数有什么用呢?我们来看一下 QWidget 的代码:
class QWidget : public QObject, public QPaintDevice
{
Q_OBJECT
Q_DECLARE_PRIVATE(QWidget)
// others
}
QWidget 是 QObject 的子类,然后看它的构造函数:
QWidget::QWidget(QWidget *parent, Qt::WindowFlags f)
: QObject(*new QWidgetPrivate, 0), QPaintDevice()
{
QT_TRY {
d_func()->init(parent, f);
} QT_CATCH(...) {
QWidgetExceptionCleaner::cleanup(this, d_func());
QT_RETHROW;
}
}
它调用了那个QObject 的 protected 构造函数,并且传入一个 QWidgetPrivate !这个 QWidgetPrivate 显然继承了 QObjectPrivate。于是我们已经明白,为什么 QWidget 中找不到 d_ptr 了,因为所有的 d_ptr 都已经在父类 QObject 中定义好了!尝试展开一下 Q_DECLARE_PRIVATE 宏,你就能够发现,它实际上把父类的 QObjectPrivate 指针偷偷地转换成了 QWidgetPrivate 的指针。这个就是前面说的 Qt 的设计思路。
分享到:
相关推荐
s9keaz128 串口升级方案 1:上位机qt5源码 2:单片机底层与应用程序源码 3:烧写文档 4:原理图
qt源码 最好用的源码 qt源码 最好用的源码 qt源码 最好用的源码
Qt编程练习:Qt实现串口调试助手,界面参考野火串口调试助手C#版,所以功能基本实现。博文地址:https://blog.csdn.net/qq_44793587/article/details/124473293
Qt源码编译./configure -prefix $PWD/qtbase -opensource 报错 ERROR: The OpenGL functionality tests failed ubuntu 14.04 LTS qt-everywhere-opensource-src-5.9.9
PCL+QT源码:增加树形控件 主要功能: - 显示多片点云,并自动更新节点信息 - 选择相关节点时,会自动更新参数(点云数量)信息 - 勾选复选框时,会显示点云,不勾选时,点云不显示。
正点QT视频配套源码:
Qt基于数据库的学生管理系统源码 Qt基于数据库的学生管理系统源码 Qt基于数据库的学生管理系统源码 Qt基于数据库的学生管理系统源码 Qt基于数据库的学生管理系统源码 Qt基于数据库的学生管理系统源码...
tcp通信Qt源码,客户端和服务器融合,可发文件和可发消息,
VS2008编译QT源码
Qt5.6.0源码,qt-everywhere-src-5.6.0-rc.zip
用Qt写成的2048游戏源码,包含pro
Qt实现窗体在显示屏旁边自动隐藏/显示,类似于QQ实现
log4qt 是 log4j 的 Qt 移植,原始项目地址(log4qt.sourceforge.net)于 2009 年起就没有更新,本次版本则是 github 上面的一个维护项目,使 log4qt 能够适用于最新版本的 Qt4 以及 Qt5。
log4qt日志源码
qt的开源项目源码qt的开源项目源码qt的开源项目源码qt的开源项目源码
Qt5综合实例:在Qt中对数据库,XML文件的建立,查询,删除和查找等操作,并且与Qt界面设计,布局,图形/图像绘制和自定义视图等功能综合起来。
C++大作业基于Qt实现的中国象棋源码。 功能: 双人对弈 人机对弈 C++大作业基于Qt实现的中国象棋源码。 功能: 双人对弈 人机对弈C++大作业基于Qt实现的中国象棋源码。 功能: 双人对弈 人机对弈C++大作业基于Qt...
这份资料包含QT教程共11章的实例源代码。方便初学者进行学习。
Qt源码窥探(1)---创建窗口过程图解。。