QGlobalStatic Struct
template <typename Holder> struct QGlobalStaticQGlobalStatic 类用于实现全局静态对象。更多
头文件: | #include <QGlobalStatic> |
CMake: | find_package(Qt6 REQUIRED COMPONENTS Core) target_link_libraries(mytarget PRIVATE Qt6::Core) |
qmake: | QT += core |
注意:该结构中的所有函数都是线程安全的。
公共类型
公共函数
bool | exists() const |
bool | isDestroyed() const |
QGlobalStatic<Holder>::Type * | operator QGlobalStatic<Holder>::Type *() |
QGlobalStatic<Holder>::Type & | operator*() |
QGlobalStatic<Holder>::Type * | operator->() |
宏
Q_GLOBAL_STATIC(Type, variableName, ...) |
详细说明
QGlobalStatic 类是在使用Q_GLOBAL_STATIC() 时导出的前端 API。请参阅该宏的文档,了解其要求和使用时机。
通常情况下,您不会直接使用该类,而是使用Q_GLOBAL_STATIC() 宏,如下所示:
Q_GLOBAL_STATIC(MyType, myGlobal)
上例创建了一个 QGlobalStatic 类型的对象,名为myGlobal
。经过上述声明后,myGlobal
对象就可以像指向MyType 类型对象的指针一样使用,并保证只初始化一次。除了作为指针使用外,该对象还提供两种方法来确定全局的当前状态:exists() 和isDestroyed()。
另请参阅 Q_GLOBAL_STATIC() 和Q_GLOBAL_STATIC_WITH_ARGS()。
成员类型文档
[alias]
QGlobalStatic::Type
该类型等同于传给Q_GLOBAL_STATIC() 或 Q_GLOBAL_STATIC_WITH_ARGS() 宏的Type
参数。它用于某些函数的返回类型。
成员函数文档
[noexcept]
bool QGlobalStatic::exists() const
如果全局静态对象已完成初始化(即该类型的构造函数已返回),但尚未完成销毁,则该函数返回true
。特别要注意的是,如果初始化仍在进行中,该函数将返回false
。
该函数一旦返回 true,就不会再返回 false,直到全局静态对象被销毁。后者发生在程序退出或包含全局静态对象的插件或库被卸载时。
在程序执行的任何时候调用该函数都是安全的:它不会失败,也不会导致死锁。此外,如果内容尚未创建,它也不会导致内容被创建。
如果可以确定全局静态对象的初始条件,并希望避免可能很昂贵的构造操作,那么该函数就很有用。
例如,在下面的示例代码中,该函数用于短路创建名为globalState
的全局静态对象,并返回一个默认值:
Q_GLOBAL_STATIC(MyType, globalState) QString someState() { if (globalState.exists()) return globalState->someState; return QString(); }
线程安全注意事项:该函数是线程安全的,因为它可以在任何时间从任何线程调用,并始终返回一个有效的回复。但由于构造的非原子性,该函数可能会在构造完成后的短时间内返回 false。
内存排序注意事项:此函数不提供任何内存排序保证。内存排序由返回指针或内容引用的访问函数提供。如果绕过访问函数并试图访问构造函数设置的某些全局状态,请务必使用QAtomicInt 或QAtomicPointer 提供的正确内存排序语义。
另请参见 isDestroyed() 。
[noexcept]
bool QGlobalStatic::isDestroyed() const
如果全局静态对象已完成销毁(即该类型的析构函数已返回),则该函数返回true
。特别要注意的是,如果销毁仍在进行中,该函数将返回false
。
该函数一旦返回 true,就不会再返回 false,直到重新启动程序或卸载并重新加载包含全局静态对象的插件或库。
在程序执行的任何时候调用该函数都是安全的:它不会失败,也不会导致死锁。此外,如果内容尚未创建,它也不会导致内容被创建。
该函数在程序关闭时执行的代码中非常有用,可用于确定是否仍可访问内容。
另请参见 exists()。
QGlobalStatic<Holder>::Type *QGlobalStatic::operator QGlobalStatic<Holder>::Type *()
该函数返回全局静态内容的地址。如果内容尚未创建,该函数将以线程安全方式创建内容。如果内容已被销毁,该函数将返回一个空指针。
例如,该函数可用于将指向全局静态内容的指针存储在局部变量中,从而避免多次调用该函数。Q_GLOBAL_STATIC() 的实现已经相当高效,但在对性能要求很高的部分,可能需要编译器提供一些帮助。例如
Q_GLOBAL_STATIC(MyType, globalState) QString someState() { if (globalState::isDestroyed()) return QString(); MyType *state = globalState; if (state->condition) return state->value; else return state->worth; }
另请参见 operator->() 和operator*()。
QGlobalStatic<Holder>::Type &QGlobalStatic::operator*()
该函数返回对该全局静态内容的引用。如果内容尚未创建,则将由该函数以线程安全的方式创建。
此函数不会检查内容是否已被销毁。如果在对象被销毁后调用此函数,它将返回一个无效引用,不得使用。
另请参见 exists() 和isDestroyed()。
QGlobalStatic<Holder>::Type *QGlobalStatic::operator->()
该函数返回全局静态内容的地址。如果内容尚未创建,则将由该函数以线程安全方式创建。
该函数不会检查内容是否已被销毁,也不会返回 null。如果在对象被销毁后调用此函数,它将返回一个不应被取消引用的悬空指针。
另请参阅 exists() 和isDestroyed()。
宏文档
Q_GLOBAL_STATIC(Type, variableName, ...)
创建一个QGlobalStatic 类型的全局静态对象,名为variableName 。它就像一个指向Type 的指针。Q_GLOBAL_STATIC 创建的对象会在首次使用时进行初始化,这意味着它不会增加应用程序或程序库的加载时间。此外,该对象在所有平台上都是以线程安全的方式初始化的。
自 Qt 6.3 起,该宏允许使用变量参数来初始化对象,因此无需Q_GLOBAL_STATIC_WITH_ARGS 。请注意,与旧的宏不同,参数不需要额外的括号。
该宏在全局上下文(即不在任何函数或类体内部)中的典型用法如下:
Q_GLOBAL_STATIC(MyType, myGlobal)
该宏旨在替换非 POD(Plain Old Data,或用 C++11 术语来说,非琐碎类型)的全局静态对象,因此得名。例如,下面的 C++ 代码创建了一个全局静态对象:
static MyType myGlobal;
与 Q_GLOBAL_STATIC 相比,假设MyType
是一个有构造函数、析构函数或其他非 POD 类型的类或结构体,后者有以下缺点:
- 需要在加载时初始化
myGlobal
(即在加载库或应用程序时调用MyType
的默认构造函数); - 即使从未使用过该对象,它也会被初始化;
- 没有确定不同翻译单元之间的初始化和销毁顺序,导致其他全局变量的构造函数或析构函数可能在初始化之前或销毁之后使用该对象。
Q_GLOBAL_STATIC 宏解决了所有这些问题,它保证了首次使用时的线程安全初始化,并允许用户查询类型是否已被销毁,从而避免了销毁后使用的问题(参见QGlobalStatic::isDestroyed() )。
构造函数和析构函数
对于 Q_GLOBAL_STATIC,当只给出一个类型和变量名时,其Type
必须是公开的默认可构造函数和公开的可析构函数。否则,Type
必须有一个公共构造函数,接受宏的其余参数。对于 Q_GLOBAL_STATIC_WITH_ARGS(),必须有一个接受宏的第三个参数作为参数列表的公共构造函数。
如果Type 的相关构造函数或析构函数是受保护的或私有的,则无法使用 Q_GLOBAL_STATIC。如果相关类型声明这些成员是受保护的,那么可以通过派生该类型并创建公共构造函数和析构函数来解决这个问题。如果该类型声明这些成员是私有的,那么在派生之前就必须先声明为友类型。
例如,以下代码就足以基于先前定义的MyOtherType
创建MyType
,该代码具有受保护的默认构造函数和/或受保护的析构函数(或声明为私有,但同时声明MyType
为好友)。
class MyType : public MyOtherType { }; Q_GLOBAL_STATIC(MyType, myGlobal)
MyType
不需要正文,因为析构函数是隐式成员,如果没有定义其他构造函数,默认构造函数也是隐式成员。但是,如果要与Type 和variableName 之后的参数一起使用,或与 Q_GLOBAL_STATIC_WITH_ARGS() 一起使用,则需要一个合适的构造函数主体:
class MyType : public MyOtherType { public: MyType(int i) : MyOtherType(i) {} }; Q_GLOBAL_STATIC(MyType, myGlobal, 42)
另外(由于 C++11 引入了继承构造函数),我们也可以写成
class MyType : public MyOtherType { public: using MyOtherType::MyOtherType; }; Q_GLOBAL_STATIC_WITH_ARGS(MyType, myGlobal, (42))
放置
Q_GLOBAL_STATIC 宏在全局作用域创建了一个类型和一个该类型的静态变量。不能将 Q_GLOBAL_STATIC 宏放在函数或类的主体中(这样做会导致编译错误)。
更重要的是,该宏应放在源文件中,而绝不能放在头文件中。由于生成的对象具有静态链接性,如果将宏放在头文件中,并被多个源文件包含,那么对象将被定义多次,而不会导致链接错误。相反,每个翻译单元将引用不同的对象,这可能导致难以追踪的微妙错误。
不推荐使用
请注意,不建议将该宏用于 POD 类型或具有 C++11 constexpr 构造函数(可构造和可析构)的类型。对于这些类型,仍建议使用常规的 static,无论是全局的还是函数本地的。
这个宏可以工作,但会增加不必要的开销。
构造时的重入性、线程安全、死锁和异常安全
Q_GLOBAL_STATIC 宏创建的对象在首次使用时会以线程安全的方式进行初始化:如果多个线程试图同时初始化对象,只有一个线程会继续初始化,而所有其他线程会等待初始化完成。
如果初始化过程中出现异常,初始化将被视为未完成,当控制权到达对象的任何用途时,将再次尝试初始化。如果有任何线程在等待初始化,其中一个线程将被唤醒以尝试初始化。
该宏不保证同一线程的重入。如果从全局静态对象自身的构造函数中直接或间接访问该对象,则肯定会发生死锁。
此外,如果两个 Q_GLOBAL_STATIC 对象在两个不同的线程上初始化,并且每个线程的初始化序列都访问了另一个线程,则可能会发生死锁。因此,建议保持全局静态构造函数的简单性,如果做不到这一点,则应确保在构造过程中全局静态的使用不存在交叉依赖关系。
销毁
如果在程序生命周期内从未使用过该对象,除了QGlobalStatic::exists() 和QGlobalStatic::isDestroyed() 函数外,将不会创建Type 类型的内容,也不会有任何退出时操作。
如果创建了对象,它将在退出时销毁,这与 Catexit()
函数类似。事实上,在大多数系统中,如果库或插件在退出前从内存中卸载,析构函数也会被调用。
由于销毁是在程序退出时进行的,因此不提供线程安全性。这包括插件或库卸载的情况。此外,由于析构函数不应抛出异常,因此也不提供异常安全功能。
不过,允许重入:在销毁过程中,可以访问全局静态对象,返回的指针与销毁开始前的指针相同。销毁完成后,除了QGlobalStatic API 中提到的情况外,不允许访问全局静态对象。
另请参阅 Q_GLOBAL_STATIC_WITH_ARGS(),Q_APPLICATION_STATIC() 和QGlobalStatic 。
© 2025 The Qt Company Ltd. Documentation contributions included herein are the copyrights of their respective owners. The documentation provided herein is licensed under the terms of the GNU Free Documentation License version 1.3 as published by the Free Software Foundation. Qt and respective logos are trademarks of The Qt Company Ltd. in Finland and/or other countries worldwide. All other trademarks are property of their respective owners.