qtcreator静态编译_Qt Creator 源码学习 06:aggregation

本文探讨了Qt Creator源码中的组件聚合概念,详细阐述了内部组件如何相互转换,并且强调了组件生命周期的绑定,一旦其中一个组件被析构,其余组件也会随之被析构。此外,文章还涉及到Qt Creator的静态编译过程。
摘要由CSDN通过智能技术生成
前面一章我们已经来到了 libs 目录。libs.pro 的 SUBDIRS 部分,第一个子项目是 aggregation。因此,我们的代码阅读也就从这里开始入手。 打开 aggregation 目录,按照之前的经验,还是从 aggregation.pro 开始。
include(../../qtcreatorlibrary.pri)DEFINES += AGGREGATION_LIBRARYHEADERS = aggregate.h \    aggregation_global.hSOURCES = aggregate.cpp
这个文件没有那么复杂。但是它的第一行还是把我们带到了另外一个文件,qtcreatorlibrary.pri。qtcreatorlibrary.pri 第一行是这样的:
include($replace(_PRO_FILE_PWD_, ([^/]+$), \\1/\\1_dependencies.pri))
前面已经介绍过 $$replace() 函数;第一个参数 _PRO_FILE_PWD_ 同样说过。这里值得说明的是后面两个参数。第二个参数 ([^/]+$) 是一个正则表达式。qmake 的正则表达式规则同 QRegExp 类似,可以参数 QRegExp 的文档。我们从内向外读。 [^/]+$ 匹配字符串的最后一个 / 字符直到最后结尾的子串; () 则是捕获匹配的子串,后面则可以使用 \1 替换。例如, _PRO_FILE_PWD_ 的值是 E:/Sources/qt-creator/src/libs/aggregation ,匹配 [^/]+$ 的部分是 aggregation ,使用 () 则将该字符串捕获到 \1 ,最后的 \\1/\\1_dependencies.pri 部分最终结果是 aggregation/aggregation_dependencies.pri$$replace() 函数替换之后的结果是 E:/Sources/qt-creator/src/libs/aggregation/aggregation_dependencies.pri 。 第二行,
TARGET = $QTC_LIB_NAME
QTC_LIB_NAME 正是在 aggregation_dependencies.pri 中定义的;可以通过打开这个文件找到具体的值。此时 TARGET 值被设置为 Aggregation 。因此,我们的类库名字就是 Aggregation。 qtcreatorlibrary.pri 文件剩下的部分没有什么新的东西,所以这里不再展开介绍。 回过头来继续看 aggregation.pro。 接下来一行,使用 DEFINES 添加了一个宏定义。后面则是所需要的三个文件:
HEADERS = aggregate.h \    aggregation_global.hSOURCES = aggregate.cpp
我们从 aggregation_global.h 开始看起。
#pragma once#include #if defined(AGGREGATION_LIBRARY)#  define AGGREGATION_EXPORT Q_DECL_EXPORT#else#  define AGGREGATION_EXPORT Q_DECL_IMPORT#endif
新版本的 Qt Creator 源代码已经抛弃了宏守卫的形式,转而使用简单的 #pragma once 预编译语句来保证头文件仅被包含一次。下面的语句定义了 AGGREGATION_EXPORT 宏,用于动态库的导出导入。由于我们在 aggregation.pro 中定义了宏 AGGREGATION_LIBRARY ,因此, AGGREGATION_EXPORT 将被定义为 Q_DECL_EXPORT 。这是 Qt 库项目的标准写法,每次使用 Qt Creator 生成代码时总会有类似的形式。 下面来看 aggregate.h。
namespace Aggregation {class AGGREGATION_EXPORT Aggregate : public QObject{    Q_OBJECT    [...]};[...]} // namespace Aggregation
类库 Aggregation 只有一个类 Aggregate 以及很多辅助函数。这些都位于 Aggregation 命名空间中。 Aggregation 命名空间包含用于“打包”相关组件的类和函数。所谓“打包”,意思是,多个组件组成一个整体,将各自的属性和行为都暴露出来。这个“打包的整体”被称为 Aggregate ,也就是 Aggregation 命名空间唯一的一个类。 Aggregation::Aggregate 将多个相关组件定义为一个聚合体,从而使外界可以将这些组件视为一个整体。这非常类似一个类实现多个接口,例如:
class Class : public Interface1, public Interface2, public Interface3{}
这里, Class 类实现了多个接口 Interface1Interface2Interface1 。外部调用者在看待 Class 类时,可以把它当做接口 Interface1Interface2Interface1 中的任意一个。换句话说,这些接口通过这个类,将自己的属性和行为暴露给外界。 虽然 C++ 支持多继承,但是,多继承通常会带来一些问题,所以一般会避免使用。 Aggregate 是类似的,只不过它不仅限于接口,从而规避了多继承的问题。 Aggregate 内部的组件可以是任意 QObject 的子类。 Aggregate 可以实现:
  • Aggregate内部的每个组件都可以相互“转换”
  • Aggregate内部的每个组件的生命周期都被绑定在一起,例如,其中任意一个被析构,那么,剩余的所有组件也就会被析构
乍看起来, Aggregate 类似于类的集合,但是上面两点是 Aggregate 与集合显著的区别。 下面是一个 Aggregate 的使用示例。
class MyInterface : public QObject { ........ };class MyInterfaceEx : public QObject { ........ };[...]MyInterface *object = new MyInterface;
在这个例子中, MyInterfaceMyInterfaceEx 是两个普通的 QObject 子类。 objectMyInterface 的一个实例。 Aggregation 命名空间提供了 query() 函数,其行为类似于 qobject_cast
Q_ASSERT(Aggregation::query(object) == object);Q_ASSERT(Aggregation::query(object) == 0);
如果我们希望 object 同样实现类 MyInterfaceEx ,但是不希望或者根本不能使用多继承,那么,我们就可以使用 Aggregate
MyInterfaceEx *objectEx = new MyInterfaceEx;Aggregation::Aggregate *aggregate = new Aggregation::Aggregate;aggregate->add(object);aggregate->add(objectEx);
Aggregate 将这两个对象“捆绑”在一起,形成一个聚合体。下面断言都是通过的:
Q_ASSERT(Aggregation::query<MyInterface>(object) == object);Q_ASSERT(Aggregation::query<MyInterfaceEx>(object) == objectEx);Q_ASSERT(Aggregation::query<MyInterface>(objectEx) == object);Q_ASSERT(Aggregation::query<MyInterfaceEx>(objectEx) == objectEx);
而删除其中任意一个,都会导致整个聚合体的析构:
delete objectEx;// 或 delete object;// 或 delete aggregate;
知道了 Aggregate 如何使用,我们就可以看它是如何实现的。
Aggregate::Aggregate(QObject *parent)    : QObject(parent){    QWriteLocker locker(&lock());    aggregateMap().insert(this, this);}
由于 AggregateQObject 的子类,所以 Aggregate 构造函数需要一个 QObject 参数。 QWriteLocker 加了一个写锁,用于保证在多线程情景下依然能够正常插入。 QWriteLocker 简化了 QReadWriteLock 锁的写操作,它需要一个 QReadWriteLock 锁作为参数,而这个 QReadWriteLock 正是 lock() 函数返回的。
QReadWriteLock &Aggregate::lock(){    static QReadWriteLock lock;    return lock;}
aggregateMap() 函数是一个私有静态函数:
[...]private:    static QHash &aggregateMap();[...]QHash &Aggregate::aggregateMap(){    static QHashmap;    return map;}
它是一个散列,保存每个 QObject 及其子类与 Aggregate 的对应关系。因为 Aggregate 本身就是 QObject ,因此使用 aggregateMap().insert(this, this); 表示 Aggregate 本身隶属于其自己这个聚合体。由于这个函数是静态的,所以所有 Aggregate 共享一个散列,起到统一注册管理的作用,无需引入第三个管理类来管理这些 Aggregate
void Aggregate::add(QObject *component){    if (!component)        return;    {        QWriteLocker locker(&lock());        Aggregate *parentAggregation = aggregateMap().value(component);        if (parentAggregation == this)            return;        if (parentAggregation) {            qWarning() << "Cannot add a component that belongs to a different aggregate" << component;            return;        }        m_components.append(component);        connect(component, &QObject::destroyed, this, &Aggregate::deleteSelf);        aggregateMap().insert(component, this);    }    emit changed();}
add() 函数用于向 Aggregate 中添加组件。添加是写操作,所以还是需要写锁。首先,查找需要添加的这个 component 是否已经在 aggregateMap() 添加,并且添加到的 Aggregate 就是 this 自己。如果是的话,不需要作任何操作,直接返回;如果不是,则会给出一个警告,“这个组件已经被添加到另外的聚合体”。如果该 component 没有被添加到任何一个聚合体,则添加到自己的 m_components 属性,并且关联销毁的信号槽,最后还需要到 aggregateMap() 注册。 m_components 的定义如下:
[...]private:    QList m_components;[...]
当对象析构时会发出 destroyed() 信号时,会调用聚合体的 deleteSelf() 槽函数。该函数是一个私有函数,其实现如下:
void Aggregate::deleteSelf(QObject *obj){    {        QWriteLocker locker(&lock());        aggregateMap().remove(obj);        m_components.removeAll(obj);    }    delete this;}
移除操作的槽函数在 QObject 对象发出 destroyed() 信号,也就是对象析构时会被调用。由于移除操作也是一种写操作,所以这里还是需要写锁。对象被析构,为避免内存泄露,我们需要自己从 aggregateMap()m_components 中将其移除。最后,为了实现聚合体内部任意组件被析构,聚合体本身也要被析构这一特性,函数最后做了 delete this; 操作。 delete this; 调用了析构函数:
Aggregate::~Aggregate(){    QList components;    {        QWriteLocker locker(&lock());        foreach (QObject *component, m_components) {            disconnect(component, &QObject::destroyed, this, &Aggregate::deleteSelf);            aggregateMap().remove(component);        }        components = m_components;        m_components.clear();        aggregateMap().remove(this);    }    qDeleteAll(components);}
Aggregate 的析构函数中,我们需要遍历 m_components 中的每一个对象,断开其关联的信号槽,然后从 aggregateMap() 中移除。需要注意的是,这里使用了 Qt 的 foreach 宏。这个宏自 Qt 5.7 已经不建议使用, 具体细节参考这里 。 QList 的清空操作的确会令人疑惑。如果 QList 中保存的是指针,就像这里,那么,清空 QList 需要两个步骤:调用 clear() 函数和 qDeleteAll() 。前者用于将指针从 QList 移除,后者会针对每一个指针都调用 delete 运算符。因为 QList 并不会持有这些指针指向的内存,所以,每一个元素都需要单独调用 delete这里比较令人不解的是,为什么要将m_components赋值给一个新的QList对象components,然后再调用qDeleteAll()。豆子这里也并不大明白,只是猜测,这是为了将qDeleteAll()的调用移动到写锁之外,毕竟这个操作不需要写同步,而delete可能会比较耗时。如果有不同意见,可以探讨一下。add() 对应的是 remove() 函数:
void Aggregate::remove(QObject *component){    if (!component)        return;    {        QWriteLocker locker(&lock());        aggregateMap().remove(component);        m_components.removeAll(component);        disconnect(component, &QObject::destroyed, this, &Aggregate::deleteSelf);    }    emit changed();}
remove() 函数用于移除聚合体中的组件。其实现与 add() 类似。在添加了写锁之后,首先从 aggregateMap() 中移除对应的组件,然后将其从自己的 m_components 中全部删除,最后再将信号槽断开连接。最后一个断开的操作是必要的,因为我们只是从聚合体中移除对象,当对象析构时,我们不希望其所在聚合体也会被析构(这是我们在 add() 操作中关联的)。 现在,我们已经分析了最重要的两个操作, add()remove() 。当组件被添加到聚合体之后,剩下的就是转换和查询。 前面我们提到,被添加到 Aggregate 中的组件可以被“转换”,其实现代码如下:
template <typename T> T *component() {    QReadLocker locker(&lock());    foreach (QObject *component, m_components) {        if (T *result = qobject_cast(component))            return result;    }    return (T *)0;}template <typename T> QList components() {    QReadLocker locker(&lock());    QList results;    foreach (QObject *component, m_components) {        if (T *result = qobject_cast(component)) {            results << result;        }    }    return results;}
这两个函数类似,只不过一个用于转换成一个对象,另外一个用于转换成一组对象。函数使用读锁来保证线程安全,通过遍历 m_components 中每一个对象,使用 qobject_cast 来判断是否所需要的类型,然后将符合的对象返回。 Aggregation 命名空间提供了全局的查询函数,用于替代 qobject_cast 。这些函数比 qobject_cast 更实用,因为它们支持 Aggregate 对象内部的查询。例如,
template  T *query(QObject *obj){    if (!obj)        return (T *)0;    T *result = qobject_cast(obj);    if (!result) {        QReadLocker locker(&Aggregate::lock());        Aggregate *parentAggregation = Aggregate::parentAggregate(obj);        result = (parentAggregation ? query(parentAggregation) : 0);    }    return result;}
query() 用于将 obj 对象转换成所需要的类型。首先,它会尝试使用 qobject_cast 进行转换,如果转换成功则直接返回,否则会查询其是否存在于某个 Aggregate 对象,如果是,则从该对象继续尝试查询。其中,两个辅助函数的实现如下:
[...]Aggregate *Aggregate::parentAggregate(QObject *obj){    QReadLocker locker(&lock());    return aggregateMap().value(obj);}[...]template <typename T> T *query(Aggregate *obj){    if (!obj)        return (T *)0;    return obj->template component();}
Aggregation 同样提供了返回 QListquery_all() 函数,这与 query() 非常类似,这里不再赘述。

b66a04f6-f244-eb11-8da9-e4434bdf6706.png

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值