创建一个全局静态对象,类型为QGlobalStatic,名称为VariableName,行为像一个指向type的指针。 Q_GLOBAL_STATIC创建的对象在第一次使用时初始化自己,这意味着它不会增加应用程序或库的加载时间。 此外,对象是在所有平台上以线程安全的方式初始化的。用法如下,在全局上下文中(即,在任何函数体之外): Q_GLOBAL_STATIC(MyType, staticType)。这个宏的目的是替换不是POD的全局静态对象(Plain Old Data,或者用c++ 11术语来说,不是由普通类型组成的),因此有了这个名字。 例如,c++代码创建了一个全局静态对象: static MyType staticType;与Q_GLOBAL_STATIC相比,假设MyType是一个具有构造函数、析构函数或其他非pod的类或结构体,上面的缺点如下:
1、它需要MyType的加载时初始化(也就是说,当库或应用程序加载时调用MyType的默认构造函数);
2、该类型将被初始化,即使它从未被使用;
3、不同单元之间的初始化和销毁的顺序是不确定的,导致可能在初始化之前或销毁之后使用;
4、如果它是在函数中找到的(也就是说,不是全局的),它将在第一次使用时被初始化,但许多当前的编译器(截至2013年)并不保证初始化将是线程安全的;
Q_GLOBAL_STATIC宏通过保证线程安全的初始化在第一次使用,并允许用户查询类型是否已经被销毁,来解决上述所有问题,以避免销毁后使用的问题(参见QGlobalStatic::isDestroyed())。
对于Q_GLOBAL_STATIC,类型type必须是可公开默认构造和可公开销毁的。 对于Q_GLOBAL_STATIC_WITH_ARGS(),必须有一个公共构造函数来匹配传递的参数。 不能将Q_GLOBAL_STATIC用于具有受保护或私有默认构造函数或析构函数的类型(对于Q_GLOBAL_STATIC_WITH_ARGS(),一个与参数匹配的受保护或私有构造函数)。 如果有问题的类型将这些成员作为受保护的成员,则可以通过从该类型派生并创建公共构造函数和析构函数来克服这个问题。 如果该类型将它们作为private,则在派生之前必须声明友元。
析构函数是隐式成员,如果没有定义其他构造函数,则默认构造函数也是隐式成员。然而,对于与Q_GLOBAL_STATIC_WITH_ARGS()一起使用,需要一个合适的构造函数体:
class MyType : public MyOtherType
{
public:
MyType(int i) : MyOtherType(i) {}
};
Q_GLOBAL_STATIC_WITH_ARGS(MyType, staticType, (42))
如果编译器支持c++ 11继承构造函数,可以这样写:
class MyType : public MyOtherType
{
public:
using MyOtherType::MyOtherType;
};
Q_GLOBAL_STATIC_WITH_ARGS(MyType, staticType, (42))
Q_GLOBAL_STATIC宏创建的类型必须是静态的,在全局作用域中。在函数中放置Q_GLOBAL_STATIC宏是不可能的(这样做将导致编译错误)。
更重要的是,这个宏应该放在源文件中,而不是头文件中。由于结果对象是静态链接的,如果宏被放置在头文件中并包含在多个源文件中,该对象将被定义多次,不会导致链接错误。相反,每个单元将指向不同的对象,这可能导致微妙且难以跟踪。
注意,不建议将该宏用于POD类型或具有c++ 11 constexpr构造函数(一般可构造和可销毁)的类型。 对于这些类型,仍然建议使用常规的静态,无论是全局的还是函数-局部的。
这个宏可以工作,但是会增加不必要的开销。
Q_GLOBAL_STATIC宏创建了一个对象,该对象在第一次使用时以线程安全的方式初始化:如果多个线程试图同时初始化该对象,只有一个线程将继续初始化,而其他所有线程将等待完成。
如果初始化过程抛出异常,则认为初始化没有完成,当控制到达任何对象的使用时,将再次尝试初始化。 如果有任何线程在等待初始化,其中一个线程将被唤醒以尝试初始化。
宏不能保证从同一个线程重入。 如果从构造函数内部直接或间接地访问全局静态对象,肯定会发生死锁。
另外,如果两个Q_GLOBAL_STATIC对象在两个不同的线程上初始化,并且每个线程的初始化序列都访问另一个,那么可能会发生死锁。 因此,建议保持全局静态构造函数简单,如果做不到,就确保在构造过程中没有交叉依赖使用全局静态函数。
如果对象在程序的生命周期内从未被使用,除了QGlobalStatic::exists()和QGlobalStatic::isDestroyed()函数外,type类型的内容将不会被创建,也不会有任何退出时操作。
如果对象被创建,它将在退出时被销毁,类似于C的atexit函数。 事实上,在大多数系统中,如果库或插件在退出之前从内存中卸载,析构函数也会被调用。
由于销毁是在程序退出时发生的,因此没有提供线程安全性。 这包括插件或库卸载的情况。 此外,由于析构函数不应该抛出异常,因此也不提供异常安全。
但是,重新调用是允许的:在销毁期间,可以访问全局静态对象,并且返回的指针将与销毁开始之前相同。 销毁完成后,不允许访问全局静态对象,除非在QGlobalStatic API中注明。
这个函数是在Qt 5.1中引入的。
Q_GLOBAL_STATIC_WITH_ARGS(Type, VariableName, Arguments)
创建一个全局静态对象,类型为QGlobalStatic,名称为VariableName,由参数arguments初始化,并作为类型的指针。Q_GLOBAL_STATIC_WITH_ARGS创建的对象在第一次使用时初始化自己,这意味着它不会增加应用程序或库的加载时间。此外,对象是在所有平台上以线程安全的方式初始化的。
用法如下,在全局上下文中(即,在任何函数体之外):
Q_GLOBAL_STATIC_WITH_ARGS(MyType, staticType, (42, "Hello", "World"))
Arguments宏形参必须始终包含括号,或者如果允许c++ 11统一初始化,则必须包含大括号。
除了使用提供的参数实际初始化内容外,这个宏的行为与Q_GLOBAL_STATIC()相同。