Qt--Q_GLOBAL_STATIC

我记得《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 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 NAME;

就是定义了一个static QGlobalStatic变量NAME

看看QGlobalStatic的定义:

template 
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,Qt5源码赏析)