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变量(其实是对象)表现得像一个指针。

原创粉丝点击