概述

所谓的全局静态对象,大多是在单例类中所见,之前写过一篇文章介绍如何实现一个单例类,在这里,这是最常见的方式来进行创建,需要自定义 static 类对象, 并进行手动初始化。而今天要说的是更简单的方式来实现,Qt 提供了一个非常方便的宏Q_GLOBAL_STATIC,可以快速创建全局静态对象。

QGlobalStatic类

其实Q_GLOBAL_STATIC宏是在QGlobalStatic中定义的,不过通常都不会直接使用QGlobalStatic类,而是使用类中定义的宏Q_GLOBAL_STATIC,与之对应的还有一个宏Q_GLOBAL_STATIC_WITH_ARGS,后面进行介绍。

基本用法

首先我们举例看看这个宏才怎么使用:

Q_GLOBAL_STATIC(MyType, globalState)
QString someState()
{if (globalState.exists())return globalState->someState;return QString();
}

这就创建了一个全局静态类对象staticType,MyType是类名,在上面的声明之后,staticType对象可以像使用指针一样使用,保证只能初始化一次。除了用作指针外,对象还提供了两种方法来确定全局的当前状态:exists()和isDestroyed()。

宏定义介绍

Q_GLOBAL_STATIC(Type, VariableName)

由Q_GLOBAL_STATIC创建的对象在首次使用时进行初始化,它不会增加应用程序或库的加载时间。此外,该对象在所有平台上都以线程安全的方式初始化的。
这个宏的典型用法如下,在全局上下文中(即,在任何函数体之外):

Q_GLOBAL_STATIC(MyType, staticType)

这个宏旨在替代不是POD的全局静态对象(普通的旧数据,或者用C ++ 11的术语,不是由平凡的类型组成),因此也就是名称。例如,下面的C ++代码创建一个全局静态:

static MyType staticType;

与Q_GLOBAL_STATIC相比,假设这MyType是一个具有构造函数,析构函数的类或结构,或者是非POD,上面写法有以下缺点:

  • 它需要加载时初始化MyType(即MyType加载库或应用程序时调用的默认构造函数);
  • 即使从未使用过,类型也会被初始化;
  • 不同的单元之间的初始化和破坏的顺序不确定,导致在初始化之前或销毁之后可能会使用;
  • 如果它是在一个函数内(即不是全局的)发现的,它将在第一次使用时被初始化,但是许多当前编译器中并不保证初始化是线程安全的;

Q_GLOBAL_STATIC宏通过在第一次使用时保证线程安全初始化并允许用户查询类型是否已被销毁以避免销毁后使用问题来解决上述所有问题。

注意:如果要使用该宏,那么累的构造函数和析构函数必须是公有的才行,如果构造函数和析构函数是私有或者受保护的类型,是不能使用该宏的。

对于具有受保护或私有默认构造函数或析构函数的类型(对于Q_GLOBAL_STATIC_WITH_ARGS(),一个与参数匹配的受保护或私有构造函数),不能使用Q_GLOBAL_STATIC 。如果将这些成员保护起来,可以通过从类型派生出来并创建公共构造函数和析构函数来解决问题。如果类型将它们设为私有,则派生之前需要声明friend。

例如,MyType基于先前定义的MyOtherType具有受保护的默认构造函数和/或受保护的析构函数

class MyType : public MyOtherType { };Q_GLOBAL_STATIC(MyType, staticType)

MyType由于析构函数是隐式成员,因此不需要定义,如果没有定义其他构造函数,则默认构造函数也是如此。但是,与Q_GLOBAL_STATIC_WITH_ARGS()一起使用时,需要一个合适的构造函数体:

 class MyType : public MyOtherType{public:MyType(int i) : MyOtherType(i) {}};Q_GLOBAL_STATIC_WITH_ARGS(MyType, staticType, (42))

该宏的声明位置

Q_GLOBAL_STATIC宏在全局范围内创建一个必须是静态的类型。无法将Q_GLOBAL_STATIC宏放在函数中(这样做会导致编译错误)。

最重要的是,这个宏应该放在源文件中,千万不要放在头文件中。由于生成的对象具有静态链接,因此如果宏放置在标题中并且被多个源文件包含,该对象将被多次定义,并且不会导致链接错误。相反,每个单元将引用一个不同的对象,这可能会导致微妙且难以追踪的错误。

线程安全,死锁和构建异常安全

Q_GLOBAL_STATIC宏创建一个对象,它在首次使用时以线程安全的方式初始化自己:如果多个线程同时尝试初始化对象,则只有一个线程会继续初始化,而其他所有线程都会等待完成。

如果初始化过程抛出异常,则认为初始化未完成,并在控制达到任何对象使用时再次尝试。如果有任何线程在等待初始化,其中一个线程将被唤醒尝试初始化。

宏不能保证来自同一线程的重入。如果全局静态对象是直接或间接从构造函数中访问的,那么肯定会发生死锁。

另外,如果两个Q_GLOBAL_STATIC对象正在两个不同的线程上初始化,并且每个初始化序列都访问另一个线程,则可能会发生死锁。出于这个原因,建议保持全局静态构造器简单,否则,确保在构造过程中不使用全局静态的交叉依赖。

销毁

如果在程序生命周期中从未使用该对象,除了QGlobalStatic :: exists()和QGlobalStatic :: isDestroyed()函数外,类型Type的内容将不会创建,并且不会有任何退出时间操作。

如果该对象被创建,它将在退出时被销毁,类似于C atexit函数。在大多数系统中,事实上,如果在退出之前将库或插件从内存中卸载,也会调用析构函数。

由于销毁是在程序退出时发生的,因此不提供线程安全性。这包括插件或库卸载的情况。另外,由于析构函数不会抛出异常,因此也不会提供异常安全性。

但是,重新调用是允许的,在销毁期间,可以访问全局静态对象,并且返回的指针与销毁开始之前的指针相同。销毁完成后,不允许访问全局静态对象,除非在QGlobalStatic API中注明。

Q_GLOBAL_STATIC_WITH_ARGS(Type, VariableName, Arguments)

该宏的用法:

Q_GLOBAL_STATIC_WITH_ARGS(MyType, staticType, (42, "Hello", "World"))

注意:宏参数需要包含在括号中。
除了使用提供的参数实际初始化内容外,该宏的行为与Q_GLOBAL_STATIC()的行为相同。

Qt之Q_GLOBAL_STATIC创建全局静态对象相关推荐

  1. qt -- Q_GLOBAL_STATIC创建全局静态对象

    创建一个全局静态对象,类型为QGlobalStatic,名称为VariableName,行为像一个指向type的指针. Q_GLOBAL_STATIC创建的对象在第一次使用时初始化自己,这意味着它不会 ...

  2. 2020-12-14(全局/静态对象的构造函数和析构函数调用的时机以及地址)

    一般的对象实例化在什么时候实例化的呢? 是不是在main函数运行到那里的时候,然后创建对象,会调用类里面的构造函数. 那当我们遇到全局/静态对象的时候,它是不是也是需要在main函数里面慢慢构造呢? ...

  3. C++构造与析构(18) - 静态对象(static object)何时销毁

    C++的static关键字 static关键字可以用于修改局部变量,函数,类的数据成员以及对象.静态局部变量只初始化一次,然后在每次函数调用时都保持它的值.静态成员函数可以直接用类来调用,不需要创建对 ...

  4. java 静态变量 new_java中静态对象和普通变量在初始化静态变量的时候有什么区别??高手!!...

    下面有一个例子,将语句(6)直接改为一个新的对象后,结果会不同,解释的清楚一些吗??豁出去了,家当10分publicclassStaticVariableTest{privatestaticStati ...

  5. CreateMutex创建互斥内核对象

    http://blog.sina.com.cn/s/blog_149e9d2ec0102wyr0.html 在编程中我们常使用程序创建一个互斥的内核对象,目的就是为了让这个程序只能运行一次.我们都会有 ...

  6. Qt:55---QT创建和使用静态链接库(.lib、.a)

    一.QT的静态链接库 QT创建一个静态链接库项目,设计各种需要导出的类,包括具有UI的窗体类.对话框类.编译后可以生成一个lib文件(MSVC编译器生成的文件后缀为".lib",M ...

  7. C++之全局对象、局部对象、静态对象详解

    1. 对于全局对象,程序一开始,其构造函数就先被执行(比程序进入点更早):程序即将结束前其析构函数将被执行. 2. 对于局部对象,当对象诞生时,其构造函数被执行:当程序流程将离开该对象的声明周期时,其 ...

  8. 创建电脑对象,电脑对象有关机和开机方法(静态对象构造方法)

    <!DOCTYPE html> <html lang="en"> <head><meta charset="UTF-8" ...

  9. 静态对象、全局对象与程序的运行机制

    静态对象.全局对象与程序的运行机制 1.   在介绍静态对象.全局对象与程序的运行机制之间的关系之前,我们首先看一下atexit函数. atexit函数的声明为:int atexit( void ( ...

最新文章

  1. 异地多活实践与设计思考点归纳
  2. 20.17 shell中的函数
  3. H3核心板开发笔记(一):编译及烧写方式
  4. git提交代码,合并同步分支
  5. 计算机网络系统容错检测,计算机系统的容错技术方法
  6. 字符驱动之按键(四:poll机制)
  7. UVa11988 Broken Keyboard (a.k.a. Beiju Text)
  8. .Net中的AOP系列之《单元测试切面》
  9. 一个比较隐蔽热门的微信解封项目
  10. 卡方检验四格表怎么做_SPSS案例实践:2*2四格表卡方检验
  11. 安卓圆形头像制作两种方法。
  12. QQ昵称字段 特殊字符的数据库存储
  13. 向量空间模型算法(Vector Space Model)
  14. 伪标签Pseudo Label
  15. javaGUI游戏教程--人物控制
  16. nginx最简单的旧域名跳转新域名
  17. 重磅!上海出落户新政:双一流应届硕士可直接落户!
  18. 开发一个商城小程序要多少钱
  19. 牛逼!java只能输入数字的正则
  20. [matlab数字图像处理8]提取一副彩色图像中红色,用HIS模型处理,RGB模型对比显示

热门文章

  1. 资讯|WebRTC M93 更新
  2. 216位攻城狮送给程序猿的10个新年礼物
  3. 白鹭引擎开发飞机大战详尽教程(四控制飞机移动)
  4. php 源码解析--count
  5. OSPF中DR和BDR作用简述
  6. 学习资源之4:Linux
  7. seaborn.FacetGrid
  8. Spring选择哪种注入方式
  9. k8s argo workflow获取登录token的命令
  10. linux sed删除文件最后一行及shell中单引号、双引号的区别