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

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

    博客分类:
  • Qt
阅读更多

下面在上一篇的基础上,我们进入Qt的源代码,看看Qt4.x是如何实现 Private Classes 的。


正如前面我们说的,或许你会看到很多类似 Q_D 或者 Q_Q 这类的宏。那么,我们来试着看一下这样的代码:

void MyClass::setFoo( int i )
{
	Q_D(MyClass);
	d->m_foo = i;
}

int MyClass::foo() const
{
	Q_D(const MyClass);
	return d->m_foo;
}


按照传统 C++ 的类,如果我们要实现这样的 getter 和 setter,我们应该使用一个私有变量 _i,然后操作这个变量。按照上一篇说的 Private Class 的做法,我们就要建一个 MyClassPrivateData 这样的类,然后使用指针对所有的数据操作进行委托。

再来看一个比较 Qt 的例子:

class MyObject: public QObject
{
    Q_OBJECT

public:
    MyObject();
    virtual ~ MyObject();
    void setMemberX( int x );
    int memberX() const;
    void setMemberY( double y);
    double memberY() const;

signals:
    void priorityChanged( MyObject::Priority priority );

private:
    int        m_memberX;
    double m_memberY;


在来看一下 Qt 的实现:

 

class MyObjectPrivate;
class MyObject: public QObject
{
    Q_OBJECT

public:
    MyObject();
    virtual ~ MyObject();
    void setMemberX( int x );
    int memberX() const;
    void setMemberY( double y);
    double memberY() const;

signals:
    void priorityChanged( MyObject::Priority priority );

protected:
    MyObjectPrivate * const d_ptr;

private:
    Q_DECLARE_PRIVATE(MyObject);
};


这个例子很简单,一个使用传统方法实现,另一个采用了 Qt4.x 的方法。Qt4.x 的方法被称为 D-Pointer,因为它会使用一个名为 d 的指针,正如上面写的那个 d_ptr。使用传统方法,我们需要在 private 里面写上所有的私有变量,通常这会让整个文件变得很长,更为重要的是,用户并不需要这些信息。而使用 D-Pointer 的方法,我们的接口变得很漂亮:再也没有那一串长长的私有变量了。你不再需要将你的私有变量一起发布出去,它们就在你的 d 指针里面。如果你要修改数据类型这些信息,你也不需要去修改头文件,只需改变私有数据类即可。


需要注意的一点是,与单纯的 C++ 类不同,如果你的私有类需要定义 signals 和 slots,就应该把这个定义放在头文件中,而不是像上一篇所说的放在 cpp 文件中。这是因为 qmake 只会检测 .h 文件中的 Q_OBJECT 宏
(这一点大家务必注意)。当然,你不应该把这样的 private class 放在你的类的同一个头文件中,因为这样做的话就没有意义了。常见做法是,定义一个 private 的头文件,例如使用 myclass_p.h 的命名方式(这也是 Qt 的命名方式)。并且记住,不要把 private 头文件放到你发布的 include 下面!因为这不是你发布的一部分,它们是私有的。然后,在你的 myclass 头文件中,使用

 

class MyClassPrivate;


这种前向声明而不是直接

 

#include "myclass_p.h"


这种方式。这也是为了避免将私有的头文件发布出去,并且前向声明可以缩短编译时间。

在这个类的 private 部分,我们使用了一个 MyClassPrivate 的 const 指针 d_ptr。如果你需要让这个类的子类也能够使用这个指针,就应该把这个 d_ptr 放在 protected 部分,正如上面的代码那样。并且,我们还加上了 const 关键字,来确保它只能被初始化一次。

下面,我们遇到了一个神奇的宏: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(MyClass) 看看展开之后是什么吧:

 

    inline MyClassPrivate* d_func() { return reinterpret_cast(qGetPtrHelper(d_ptr)); }
    inline const MyClassPrivate* d_func() const { return reinterpret_cast(qGetPtrHelper(d_ptr)); }
    friend class MyClassPrivate;


它实际上创建了两个 inline 的 d_func() 函数,返回值分别是我们的 d_ptr 指针和 const 指针。另外,它还把 MyClassPrivate 类声明为 MyClass 的 friend。这样的话,我们就可以在 MyClass 这个类里面使用 Q_D(MyClass) 以及 Q_D(const MyClass)。还记得我们最先看到的那段代码吗?现在我们来看看这个 Q_D 倒是是何方神圣!

 

// A template function for getting the instance to your private class instance.
template  static inline T *qGetPtrHelper(T *ptr) { return ptr; }

// A macro for getting the d-pointer
#define Q_D(Class) Class##Private * const d = d_func()

 

下面还是自己展开一下这个宏,就成了

 

MyClassPrivate * const d = d_func()

 

简单来说,Qt 为我们把从 d_func() 获取 MyClassPrivate 指针的代码给封装起来了,这样我们就可以比较面向对象的使用 getter 函数获取这个指针了。

 

现在我们已经比较清楚的知道 Qt 是如何使用 D-Pointer 实现我们前面所说的信息隐藏的了。但是,还有一个问题:如果我们把大部分代码集中到 MyClassPrivate 里面,很可能需要让 MyClassPrivate 的实现访问到 MyClass 的一些东西。现在我们让主类通过 D-Pointer 访问 MyClassPrivate 的数据,但是怎么反过来让 MyClassPrivate 访问主类的数据呢?Qt 也提供了相应的解决方案,那就是 Q_Q 宏,例如:

 

class MyObjectPrivate
{
public:
    MyObjectPrivate(MyObject * parent):
            q_ptr( parent ),
            m_priority(MyObject::Low)
    {}
    void foo()
    {
        // Demonstrate how to make MyObject to emit a signal
        Q_Q(MyObject);
        emit q->priorityChanged( m_priority );
    }

    //  Members
    MyObject * const q_ptr;
    Q_DECLARE_PUBLIC(MyObject);
    MyObject::Priority m_priority;
};

 

在 private 类 MyObjectPrivate 中,通过构造函数将主类 MyObject 的指针传给 q_ptr。然后我们使用类似主类中使用的 Q_DECLARE_PRIVATE 的宏一样的另外的宏 Q_DECLARE_PUBLIC。这个宏所做的就是让你能够通过 Q_Q(Class) 宏使用主类指针。与 D-Pointer 不同,这时候你需要使用的是 Q_Pointer。这两个是完全相对的,这里也就不再赘述。

 

现在我们已经能够使用比较 Qt 的方式来使用 Private Classes 实现信息隐藏了。这不仅仅是 Qt 的实现,当然,你也可以不用 Q_D 和 Q_Q,而是使用自己的方式,这些都无关紧要。最主要的是,我们了解了一种 C++ 类的设计思路,这是 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

    log4qt qt5版本

    你可以通过Qt的包管理工具或者手动下载源码编译来获取log4qt。在工程文件.pro中添加对应的库依赖,例如: ```pro QT += core gui greaterThan(QT_MAJOR_VERSION, 4): QT += widgets LIBS += -llog4qt ``` 这样,你就...

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

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

    QT6源码编译所需要的工具

    2. **获取QT源码**: - 打开命令行或Git Bash,使用`git clone`命令从QT官方仓库克隆源码: ``` git clone https://code.qt.io/cgit/qt/qtbase.git ``` 3. **配置环境变量**: - 将CMake、Python和Git的安装...

    VS2008编译QT源码

    2. 在编译QT源码之前,需要设置环境变量,包括 Path 变量和 QTDIR 变量。Path 变量用于指定QT的bin目录,QTDIR 变量用于指定QT的安装目录。 3. 在 configure 阶段,需要使用 vsvars32.bat 设置VS2008的环境变量,...

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

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

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

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

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

    - **使用官方提供的组合软件包**:官方提供了一个包含QT源码和tslib源码的软件包 `qt-4.8.7_tslib-1.1.tar.bz2`,位于路径 `TQIMX6UL_COREB_CD_V1.0_R2.1_20171115\Linux 源码包\Qt 源码`。 - **自行下载**:也可以...

    Qt-5.12.12源码

    Qt-5.12.12源码是Qt框架的一个具体版本,发布于2019年,它包含了构建Qt库和应用程序所需的所有源代码。这个版本在之前的版本基础上进行了优化和修复,提供了更稳定、更高效的功能。 首先,让我们深入理解Qt的核心...

    2048游戏Qt源码

    通过学习这个源码,你可以了解到Qt GUI编程的基本结构,以及如何实现一个简单的2D游戏逻辑。同时,它还涉及到事件处理、对象间的通信、以及资源管理等重要概念。对于想要提升Qt编程技能或对游戏开发感兴趣的人来说,...

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

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

    正点QT视频配套源码:

    在这个“正点QT视频配套源码”中,我们可以预见到一系列与QT相关的编程实例和示例代码。这些源码通常是为了配合教学视频,帮助学习者更好地理解QT编程的概念和实践技巧。以下是一些可能涵盖的知识点: 1. **QT基本...

    log4qt源码

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

    qt项目实项目源码.zip

    qt项目实项目源码.zipqt项目实项目源码.zipqt项目实项目源码.zipqt项目实项目源码.zipqt项目实项目源码.zipqt项目实项目源码.zipqt项目实项目源码.zipqt项目实项目源码.zipqt项目实项目源码.zipqt项目实项目源码....

    基于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的一个主要...

    经典书籍C++GuiQt4 源码qt5兼容版

    《C++ Gui Qt4》是一本专注于C++与Qt库结合开发图形用户界面的经典书籍,其源码已更新为Qt5兼容版本。Qt是一个跨平台的C++图形用户界面应用程序开发框架,广泛应用于Unix/Linux、Windows以及Mac OS X等操作系统。这...

Global site tag (gtag.js) - Google Analytics