`
Finder
  • 浏览: 27390 次
  • 性别: Icon_minigender_1
  • 来自: 南京
社区版块
存档分类
最新评论

Qt源码剖析:信息隐藏(3)

    博客分类:
  • Qt
阅读更多

前面我们已经看到了怎样使用标准的 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 的设计思路。

分享到:
评论

相关推荐

    qt源码 最好用的源码

    这个“qt源码 最好用的源码”压缩包可能包含了完整的QT库源代码,这对于开发者深入理解其工作原理、进行二次开发或者优化性能有着极高的价值。 QT库的核心特性包括: 1. **跨平台性**:QT支持多种操作系统,如...

    解决Qt源码编译报The OpenGL functionality tests failed

    Qt源码编译./configure -prefix $PWD/qtbase -opensource 报错 ERROR: The OpenGL functionality tests failed ubuntu 14.04 LTS qt-everywhere-opensource-src-5.9.9

    在linux下编译Qt源码,测试环境乌班图

    另外,提供的文件"编译Qt源码.pdf"可能是编译过程的详细指南或注意事项,建议阅读以获取更具体的信息。而".emmx"文件格式不常见,可能是一种特定的思维导图文件,与编译过程关系不大,如果需要打开,确保证明已安装...

    VS2008编译QT源码

    "VS2008编译QT源码" 以下是对标题、描述、标签和部分内容的详细解释和知识点总结: 标题和描述 VS2008编译QT源码是指使用Visual Studio 2008编译QT源代码的过程。QT是一个开源的跨平台应用程序开发框架,由 ...

    QT6源码编译所需要的工具

    QT6源码编译是一个复杂的过程,涉及到许多步骤和技术细节。在Visual Studio 2019环境下编译QT6.1.3版本,你需要准备一些必要的工具和了解关键的编译流程。以下是一份详尽的指南: 1. **安装必备软件**: - **...

    Qt编程练习:Qt实现串口调试助手

    Qt编程练习:Qt实现串口调试助手,界面参考野火串口调试助手C#版,所以功能基本实现。博文地址:https://blog.csdn.net/qq_44793587/article/details/124473293

    PCL+QT源码:增加树形控件

    首先,让我们了解“PCL+QT源码:增加树形控件”这一主题的核心概念。这里的“树形控件”通常指的是QT中的QTreeWidget或QTreeView,它们允许用户以层次结构的方式显示数据。在PCL的上下文中,这可以用于展示和管理...

    Qt实现窗体在显示屏旁边自动隐藏/显示

    "Qt实现窗体在显示屏旁边自动隐藏/显示"这个话题涉及到的是如何让一个Qt窗口根据用户的操作或者系统状态,智能地出现在屏幕边缘或者隐藏起来,提供一种类似QQ等即时通讯软件的用户体验。这种功能可以增加应用的便捷...

    交叉编译QT4.8.7源码生成qmake工具.pdf

    ### 交叉编译QT4.8.7源码生成qmake工具 #### 一、概述 本文档将详细介绍如何从零开始交叉编译QT4.8.7源代码以生成`qmake`工具的过程。这一过程对于那些希望在特定嵌入式系统(如TQ-i.MX6UL)上开发QT应用程序的...

    2048游戏Qt源码

    【标题】"2048游戏Qt源码"是一个基于Qt框架实现的2048游戏的编程项目,它展示了如何使用Qt库来构建一个功能完整的游戏应用。Qt是一个跨平台的应用程序开发框架,广泛应用于桌面和移动平台,提供丰富的图形用户界面...

    Qt基于数据库的学生管理系统源码.zip

    Qt基于数据库的学生管理系统源码 Qt基于数据库的学生管理系统源码 Qt基于数据库的学生管理系统源码 Qt基于数据库的学生管理系统源码 Qt基于数据库的学生管理系统源码 Qt基于数据库的学生管理系统源码...

    Qt-5.12.12源码

    3. **Widgets模块**:基于QtGui,提供了传统的窗口小部件,如QMainWindow、QWidget、QPushButton等,用于构建桌面应用程序。 4. **Network模块**:提供网络编程接口,如TCP/UDP套接字、HTTP/FTP协议,便于进行网络...

    正点QT视频配套源码:

    3. 修改源码:尝试修改部分代码,看看会有什么变化,增强对QT编程的理解。 4. 实践项目:将学到的知识应用到自己的项目中,巩固技能。 这个配套源码集合是一个宝贵的资源,可以帮助初学者快速掌握QT开发,同时也是...

    log4qt源码

    《深入解析log4qt日志库源码》 在软件开发中,日志记录是必不可少的环节,它能够帮助开发者追踪程序运行状态,定位错误,优化性能。Log4Qt是Qt框架下的一个强大的日志系统,它借鉴了Java界的log4j设计模式,提供了...

    基于qt的汽车销售管理系统(源码+数据库脚本).rar

    开发者可能会使用Qt的网络模块处理用户认证,同时利用数据库存储用户信息。权限控制则可能通过角色(Role-based Access Control, RBAC)模型实现,确保不同级别的用户只能访问其权限范围内的功能。 4. **销售管理**...

    Qt+Cutelyst学习笔记(二十八)win10+Qt5.15.2+qmake接收表单提交的文件 示例源码

    Qt+Cutelyst学习笔记(二十八)win10+Qt5.15.2+qmake接收表单提交的文件 示例源码 https://blog.csdn.net/aggs1990/article/details/124128783 CSDN审核可能较慢,如无法下载,可以过段时间再回来看下

    Qt示例程序源码.rar_QT5_Qt 程序 源码_qt源码_源码 QT

    在本资源中,我们关注的是Qt5.8的示例程序源码,这对于学习和理解Qt框架的功能和用法非常有帮助。Qt是一个跨平台的应用程序开发框架,它使用C++语言,支持Windows、Linux、macOS等多种操作系统。Qt5是Qt的一个主要...

    QT-侧靠动画窗口-隐藏和显示窗体

    3. **信号与槽机制**:QT的信号与槽机制是事件处理的核心,通过定义信号和槽函数,我们可以响应用户操作,比如点击按钮,触发窗体的隐藏和显示动画。 4. **事件过滤器**:为了监听用户与窗体边缘的交互,我们可以为...

    log4qt 源码工程文件

    log4qt 是 log4j 的 Qt 移植,原始项目地址(log4qt.sourceforge.net)于 2009 年起就没有更新,本次版本则是 github 上面的一个维护项目,使 log4qt 能够适用于最新版本的 Qt4 以及 Qt5。

Global site tag (gtag.js) - Google Analytics