Qt--Q_GLOBAL_STATIC
来源:互联网 发布:下载图片编辑软件 编辑:程序博客网 时间:2024/06/05 02:42
我记得《Effective C++》中有这么一条:
条款02 对于单纯常量,最好以const对象或enum替换#define;对于形似函数的宏,最好改用inline函数替换#define
但是Qt中却将define玩的出神入化,宏定义和泛型编程的结合更是令我大开眼见。
本节以Q_GLOBAL_STATIC为例来赏析下Qt中的宏艺术。
Q_GLOBAL_STATIC(TYPE,NAME)宏用来声明定义一个全局的静态变量,一般我们定义全局静态变量如下:
static MyType varname;
也很简单,那Qt中为何要大费周章去定义一个Q_GLOBAL_STATIC宏了,自然是有妙用的。
以上定义语句有如下缺点:
- 它需要MyType的加载时间初始化(也就是说,当库或应用程序加载时,MyType的默认构造函数被调用);
- 变量将被初始化,即使它从未被使用;
- 不同编译器的初始化和销毁顺序不确定,导致初始化前或销毁后变量被使用;
- 如果在函数中定义它(即不是全局的),它将在第一次使用时初始化,但是许多当前编译器并不能保证初始化的过程是线程安全的;
Q_GLOBAL_STATIC宏定义的变量则消除了这些问题,仅在第一次调用时才构造和初始化,节省了应用程序初始化加载的时间,确保了是线程安全的。
让我们分析下源码,看看Q_GLOBAL_STATIC是如何实现并解决这些问题的。
#define Q_GLOBAL_STATIC_WITH_ARGS(TYPE, NAME, ARGS) \namespace { namespace Q_QGS_ ## NAME { \ typedef TYPE Type; \ QBasicAtomicInt guard = Q_BASIC_ATOMIC_INITIALIZER(QtGlobalStatic::Uninitialized); \ Q_GLOBAL_STATIC_INTERNAL(ARGS) \} } \static QGlobalStatic<TYPE, \ Q_QGS_ ## NAME::innerFunction, \ Q_QGS_ ## NAME::guard> NAME;#define Q_GLOBAL_STATIC(TYPE, NAME) \ Q_GLOBAL_STATIC_WITH_ARGS(TYPE, NAME, ())
Q_GLOBAL_STATIC只是将Q_GLOBAL_STATIC_WITH_ARGS中的ARGS替换成了()
Q_GLOBAL_STATIC_WITH_ARGS中利用了全局变量名NAME是唯一的这一特性,构造了一个独有的命名空间,在Q_QGS_ ## NAME命名空间中定义了一个guard 用来监视状态,下面是几种监视状态的定义:
enum GuardValues { Destroyed = -2, Initialized = -1, Uninitialized = 0, Initializing = 1};
使用Q_GLOBAL_STATIC_INTERNAL宏定义了一个innerFunction函数
#define Q_GLOBAL_STATIC_INTERNAL(ARGS) \ inline Type *innerFunction() \ { \ struct HolderBase { \ ~HolderBase() Q_DECL_NOTHROW \ { if (guard.load() == QtGlobalStatic::Initialized) \ guard.store(QtGlobalStatic::Destroyed); } \ }; \ static struct Holder : public HolderBase { \ Type value; \ Holder() \ Q_DECL_NOEXCEPT_EXPR(noexcept(Type ARGS)) \ : value ARGS \ { guard.store(QtGlobalStatic::Initialized); } \ } holder; \ return &holder.value; \ }
这个函数中定义了一个static Holder变量,即局部static变量,Holder中包裹了我们要定义的类型变量
Type value;
返回值就是这个变量的指针
return &holder.value;
局部static变量在函数第一次调用时初始化,并调用了Type的构造函数
: value ARGS
ARGS被用来传入Type构造函数所需的参数,Q_GLOBAL_STATIC的ARGS是空括号(),表示调用Type的无参构造函数,如果我们使用Q_GLOBAL_STATIC_WITH_ARGS给它参数,就是调用对应的带参构造函数了。
Holder构造函数中将guard状态置为QtGlobalStatic::Initialized,析构时置为QtGlobalStatic::Destroyed
最后一句
static QGlobalStatic<TYPE, \ Q_QGS_ ## NAME::innerFunction, \ Q_QGS_ ## NAME::guard> NAME;
就是定义了一个static QGlobalStatic变量NAME
看看QGlobalStatic的定义:
template <typename T, T *(&innerFunction)(), QBasicAtomicInt &guard>struct QGlobalStatic{ typedef T Type; bool isDestroyed() const { return guard.load() <= QtGlobalStatic::Destroyed; } bool exists() const { return guard.load() == QtGlobalStatic::Initialized; } operator Type *() { if (isDestroyed()) return 0; return innerFunction(); } Type *operator()() { if (isDestroyed()) return 0; return innerFunction(); } Type *operator->() { Q_ASSERT_X(!isDestroyed(), "Q_GLOBAL_STATIC", "The global static was used after being destroyed"); return innerFunction(); } Type &operator*() { Q_ASSERT_X(!isDestroyed(), "Q_GLOBAL_STATIC", "The global static was used after being destroyed"); return *innerFunction(); }};
模板参数传入真正的类型TYPE,刚刚在G_QGS_##NAME命名空间中定义的innerFunction函数,和guard状态变量,重载了括号()、指针->、取值操作符*,使得NAME变量(其实是对象)表现得像一个指针。
- Qt--Q_GLOBAL_STATIC
- Q_GLOBAL_STATIC
- QT
- QT
- QT
- Qt
- qt
- QT
- QT
- QT
- QT
- qt
- Qt
- QT
- qt
- qt
- Qt
- Qt
- Spring 定时任务之 @Scheduled cron表达式
- jqweui的picker动态加载数据
- 利用 socket.io 实现消息实时推送
- 第一次构建个人网站的记录
- 【图论】点分治总结&POJ2114Boatherds题解
- Qt--Q_GLOBAL_STATIC
- centos一键安装redmine
- PSR-4自动加载器
- servlet请求转发、重定向路径
- VUE项目目录介绍
- DNS域名系统
- BZOJ2152: 聪聪可可 【点分治】
- SpringBoot定时任务说明
- 程序员着装的改变