添加链接
link之家
链接快照平台
  • 输入网页链接,自动生成快照
  • 标签化管理网页链接

在C++中,static表示的是“静态初始化”,由其声明的变量因此也叫作“静态变量”,他们从完成初始化后就一直存在于程序运行空间中(确切地说位于静态变量区),直至程序退出或销毁。

如果按照变量的作用域来划分,静态变量可以分为3类:

  • global variable,即全局变量
  • static variable with file scope
  • static variable with block scope
  • 前两种不必多说,重点说一下第三种里的“block scope”,它可以表示类内部、函数内部,或者仅仅是由“{}”包括起来的一个code block。

    同时,按照静态变量初始化的时机,初始化过程可分为: 编译时初始化 加载(运行)时初始化 ,前者主要发生在静态常量的编译过程中,如程序中“static int a = 3”因为此处3为常量,编译时就能确定,因此这里就发生的是编译时初始化,反之,如果不是编译时初始化,那就必定进行的是加载时初始化。

    static变量经常被用来实现单例模式。在设计模式中,单例模式广为人知,即使没有系统学习过设计模式的人很可能也对单例模式不会陌生。设计实现一个单例模式并非难事,但在多线程并发环境下这个问题就貌似不那么简单了。
    通常来说,一个单例模式的类中都包含一个指向static类实例的指针,并提供一个公共接口返回这个指针以实现对这个实例的调用。因为在程序整个执行周期中,static变量只加载一次,保证了“仅此一个”的事实,如下代码所示:

    #1    class A {
    #2    public:
    #3        static A* getInstance() {
    #4            if (instance_ == NULL) 
    #5                instance_ = new A();
    #6            return instance_;
    #7        }
    #8    private:
    #9        static A* instance_;
    #10    }
    #11    //static变量在类外定义
    #12    A* A::instance_ = NULL;

    如上代码就实现了一个简单的C++单例模式。在单线程程序中,这个单例类没有任何问题,但是在多线程环境下,很容易就能发现方法getInstance中存在race condition,如果线程A运行到了#4,此时instance_为NULL,这时线程B也运行到了这里,instance_此时还未进行构造,它的值还是NULL,那么接下来两个线程将分别执行new A()的操作,本为单例的类实际上并非单例。为了克服这个race condition,人们尝试用加锁、双阶段加锁等方法来解决它,虽然可以实现线程安全性,但毕竟增加了代码量以及复杂性,性能也受到了一定的影响。因此又有了另一种方法来实现单例类,如下代码所示:

    #1    class A {
    #2    public:
    #3        static A* getInstance() {
    #4            static A instance_;
    #5            return &instance_;
    #6        }
    #7    }

    根据static的语义,其只在程序对类A加载时进行一次初始化,全局只有这一个实例。看起来很美好,而且代码更少了。但是这实际上是不对的,对于在编译时进行初始化的static变量,它一定是线程安全的,但是对于这种加载时进行初始化的变量,编译器生成的代码实际上类似这样:

    static bool initialized = false;
    static A instance_;
    if (initialized == false) {
        initialized = true;
        instance_ = A();
    return &instance_;

    看到了吗?实际上还是会有race condition。那么如何在不加锁、不引入复杂设计的情况下实现单例模式呢?
    还是要感谢C++的发展,上述代码是在C++11以前的标准中得到的,而在C++11标准中,static的语义实际上已经是线程安全的了。引用C++11标准中的内容:

    If control enters the declaration concurrently while the variable is being initialized,the concurrent execution shall wait for completion of the initialization.

    在网站cppreference中,也有类似的描述:

    If multiple threads attempt to initialize the same static local variable concurrently,the initialization occurs exactly once(since c++11)

    因此,我认为在C++11中,以上代码是可以实现一个线程安全的单例类的。

    互联网先驱者 运营 @ 北京中软国际科技服务有限公司 727.8k
    粉丝