一、静态模式不是单一的情况下,

  刚开始学习的人可能误, 误以为所有的成员变量和成员方法用于 static , 就是单例模式了:

class Singleton

{

public:

    /* static method */

    

private:

    static Singleton m_data; //static data member 在类中声明。在类外定义

};



Singleton Singleton::m_data;
    
    乍一看确实具备单例模式的非常多条件, 只是它也有一些问题. 第一, 静态成员变量初始化顺序不依赖构造函数, 得看编译器心情的, 没法保证初始化顺序 (极端情况: 有 a b 两个成员对象, b 须要把 a 作为初始化參数传入,
你的类就 必须 得要有构造函数, 并确保初始化顺序).

  第二, 最严重的问题, 失去了面对对象的重要特性 -- "多态", 静态成员方法不可能是 virtual 的(补充一点,静态成员方法也不可能是 const 的. Singleton类的子类没法享受 "多态" 带来的便利.


  二、饿汉模式

  饿汉模式 是指单例实例在程序执行时被马上执行初始化:

class Singleton

{

public:

    static Singleton& getInstance()

    {

        return m_data;

    }

    

private:

    static Singleton m_data; //static data member 在类中声明。在类外定义

    Singleton(){}

    ~Singleton(){}

};



Singleton Singleton::m_data;

  

    这样的模式的问题也非常明显, 类如今是多态的, 但静态成员变量初始化顺序还是没保证: 假如有两个单例模式的类 ASingleton 和 BSingleton, 某天你想在 BSingleton 的构造函数中使用 ASingleton 实例,
这就出问题了. 由于 BSingleton m_data 静态对象可能先 ASingleton 一步调用初始化构造函数, 结果 ASingleton::getInstance() 返回的就是一个未初始化的内存区域, 程序还没跑就直接崩掉。

恩。这仅仅是理论分析的结果。以下给出一个简单的样例说明一下问题所在吧!


    实例:ASingleton、BSingleton两个单例类。当中 ASingleton 的构造函数中使用到 BSingleton 的单例对象。

class ASingleton

{

public:

    static ASingleton* getInstance()

    {

        return &m_data;

    }

    void do_something()

    {

        cout<<"ASingleton do_something!"<<endl;

    }

protected:

    static ASingleton m_data; //static data member 在类中声明。在类外定义

    ASingleton();

    ~ASingleton() {}

};



class BSingleton

{

public:

    static BSingleton* getInstance()

    {

        return &m_data;

    }

    void do_something()

    {

        cout<<"BSingleton do_something!"<<endl;

    }

protected:

    static BSingleton m_data; //static data member 在类中声明,在类外定义

    BSingleton();

    ~BSingleton() {}

};



ASingleton ASingleton::m_data;

BSingleton BSingleton::m_data;



ASingleton::ASingleton()

{

    cout<<"ASingleton constructor!"<<endl;

    BSingleton::getInstance()->do_something();

}


BSingleton::BSingleton()

{

    cout<<"BSingleton constructor!"<<endl;

}
    
    在这个測试样例中,我们将上述代码放在一个 main.cpp 文件里,当中 main 函数为空。

int main()

{

    ;

}
   
     执行測试结果是:

ASingleton constructor!

BSingleton do_something!

BSingleton constructor!

奇怪了,为什么 BSingleton 的构造函数竟然是在成员函数 do_something 之后调用的?

以下进行分析:
    首先我们看到这个測试用例中。因为仅仅有一个源文件,那么按从上到下的顺序进行编译执行。注意到:

ASingleton ASingleton::m_data;

BSingleton BSingleton::m_data;
    
 
  这两个定义式,那么就会依次调用 ASingleton 的构造函数 和 BSingleton 的构造函数进行初始化。

    一步一步来,首先是 ASingleto 的 m_data。
    那么程序就会进入 ASingleton 的构造函数中运行,即:

ASingleton::ASingleton()

{

    cout<<"ASingleton constructor!"<<endl;

    BSingleton::getInstance()->do_something();

}
    
    首先运行 cout,然后接着要获取 BSingleton 的单例,尽管说 BSingleton 的定义尚未运行。即  BSingleton BSingleton::m_data; 语句尚未运行到。可是
BSingleton 类中存在着其声明,那么还是能够调用到其 do_something 方法的。

    ASingleton 的构造函数运行完成。那么 ASingleton ASingleton::m_data;  语句也就运行结束了,即
ASingleton 单例对象 m_data 也就初始化完毕了。


    接下来运行 BSingleton BSingleton::m_data; 语句,那么也就是运行
BSingleton 的构造函数了。
    所以就有了终于结果的输出了。


那么到此,我们也许会说:既然 ASingleton 的构造函数中要用到 BSingleton 单例对象,那么就先初始化 BSingleton 的单例对象咯,是的,我们能够调换一下顺序:

//ASingleton ASingleton::m_data;

//BSingleton BSingleton::m_data;

//改动成:

BSingleton BSingleton::m_data;

ASingleton ASingleton::m_data;

  再执行一下,会发现输出的结果就正常了。

ASingleton constructor!

BSingleton constructor!

BSingleton do_something!
  
    问题攻克了。那么我们通过这个问题实例,我们对于 静态成员变量 初始化顺序没有保障 有了更深刻的理解了。

    在这个简单的样例中。我们通过调换代码位置能够保障 静态成员变量 的初始化顺序。可是在实际的编码中是不可能的。class 文件声明在头文件(.h)中,class 的定义在源文件(.cpp)中。而类静态成员变量声明是在 .h 文件里。定义在 .cpp 文件里。那么其初始化顺序就全然依靠编译器的心情了。所以这也就是 类静态成员变量
实现单例模式的致命缺点。
    当然。假如不出现这样的:在某单例的构造函数中使用到还有一个单例对象  的使用情况。那么还是能够接受使用的。

    三、懒汉模式:单例实例仅仅在第一次被使用时进行初始化:

class Singleton

{

public:

    static Singleton* getInstance()

    {

        if(! m_data) m_data = new Singleton();

        return m_data;

    }

    

private:

    static Singleton* m_data; //static data member 在类中声明,在类外定义

    Singleton(){}

    ~Singleton(){}

};



Singleton* Singleton::m_data = nullptr; 

 getInstance() 仅仅在第一次被调用时为 m_data 分配内存并初始化. 嗯, 看上去全部的问题都攻克了, 初始化顺序有保证, 多态也没问题.

    可是仅仅是看似没有问题而已,事实上当中存在着两个问题:

①线程不安全:我们注意到在 static Singleton* getInstance() 方法中,是通过 if 语句推断 静态实例变量 是否被初始化来认为是否进行初始化,那么在多线程中就有可能出现多次初始化的问题。

例如说,有两个多线程同一时候进入到这种方法中,同一时候运行 if 语句的推断,那么就会出现两次两次初始化静态实例变量的情况。


②析构函数没有被运行: 程序退出时, 析构函数没被运行. 这在某些设计不可靠的系统上会导致资源泄漏, 比方文件句柄, socket 连接, 内存等等. 幸好 Linux / Windows 2000/XP 等经常使用系统都能在程序退出时自己主动释放占用的系统资源. 只是这仍然可能是个隐患。
  对于这个问题, 比較土的解决方法是, 给每一个 Singleton 类加入一个 destructor() 方法:

virtual bool destructor()

{

    // ... release resource

    if (nullptr != m_data)

    {

        delete m_data;

        m_data = nullptr;

    }  

}

  

    然后在程序退出时确保调用了每一个 Singleton 类的 destructor() 方法, 这么做尽管可靠, 但却非常是繁琐.

  
    四、懒汉模式改进版:使用局部静态变量

class Singleton {

public:

    static Singleton& getInstance() {

        static Singleton theSingleton;

        return theSingleton;

    }

    /* more (non-static) functions here */

 

private:

    Singleton();                            // ctor hidden

    Singleton(Singleton const&);            // copy ctor hidden

    Singleton& operator=(Singleton const&); //
assign op. hidden

    ~Singleton();                           // dtor hidden

};

可是这样的方式也存在着非常多的问题:
①随意两个单例类的构造函数不能相互引用对方的实例,否则会导致程序崩溃。如:

ASingleton& ASingleton::getInstance() {

    const BSingleton& b = BSingleton::getInstance();

    static ASingleton theSingleton;

    return theSingleton;

}

 

BSingleton& BSingleton::getInstance() {

    const ASingleton & b = ASingleton::getInstance();

    static BSingleton theSingleton;

    return theSingleton;

}

②多个 Singleton 实例相互引用的情况下, 须要慎重处理析构函数. 如: 初始化顺序为 ASingleton »BSingleton » CSingleton 的三个
Singleton 类, 当中 ASingleton BSingleton 的析构函数调用了CSingleton 实例的成员函数,
程序退出时, CSingleton 的析构函数 将首先被调用, 导致实例无效, 那么兴许 ASingleton BSingleton 的析构都将失败,
导致程序异常退出.

③在局部作用域下的静态变量在编译时,编译器会创建一个附加变量标识静态变量是否被初始化,会被编译器变成像以下这样(伪代码):

static Singleton &Instance()

{

  static bool constructed = false;

  static uninitialized Singleton instance_;

  if (!constructed) {

    constructed = true;

    new(&s) Singleton; //construct it

  }

  return instance_;

}

    那么,在多线程的应用场合下必须小心使用. 假设唯一实例尚未创建时, 有两个线程同一时候调用创建方法, 且它们均没有检測到唯一实例的存在, 便会同一时候各自创建一个实例, 这样就有两个实例被构造出来, 从而违反了单例模式中实例唯一的原则.
解决问题的办法是为指示类是否已经实例化的变量提供一个相互排斥锁 (尽管这样会减少效率).

加锁例如以下:

static Singleton &getInstance()

{

    Lock();

    //锁自己实现 static

    Singleton instance_;

    UnLock();

 

    return instance_;

}
    但这样每次调用instance()都要加锁解锁。代价略大。

五、终极方案

    在前面的讨论中。单例类中的静态对象不管是作为静态局部对象还是作为类静态全局变量都有问题。那么有什么更好的解决方式呢?
    boost 的实现方式是:单例对象作为静态局部变量。然后添加一个辅助类,并声明一个该辅助类的类静态成员变量,在该辅助类的构造函数中。初始化单例对象。
实现例如以下:

class Singleton

{

public:

    static Singleton* getInstance()

    {

        static Singleton instance;

        return &instance;

    }

    

protected:

    struct Object_Creator

    {

        Object_Creator()

        {

            Singleton::getInstance();

        }

    };

    static Object_Creator _object_creator;



    Singleton() {}

    ~Singleton() {}

};

Singleton::Object_Creator Singleton::_object_creator;

   
在前面的方案中:饿汉模式中,使用到了类静态成员变量,可是遇到了初始化顺序的问题; 懒汉模式中。使用到了静态局部变量,可是存在着线程安全等问题。

    那么在这个终极方案中能够说综合了以上两种方案。即採用到了类静态成员变量,也採用到了静态局部变量。

    注意到当中的辅助结构体 Object_Creator (能够称之为 proxy-class)所声明的类静态成员变量,初始化该静态成员变量时,当中的构造函数 调用了单例类的 getInstance
方法。

这样就会调用到 Singleton::getInstance()
方法初始化单例对象。那么自然 Singleton 的构造函数也就运行了。


    我们能够在Singleton 和 Object_Creator 的构造函数中加入一些输出信息:

class Singleton

{

public:

    static Singleton* getInstance()

    {

        static Singleton instance;

        return &instance;

    }



protected:

    struct Object_Creator

    {

        Object_Creator()

        {

            cout<<"Object_Creator constructor"<<endl;

            Singleton::getInstance();

        }

    };

    static Object_Creator _object_creator;



    Singleton() {cout<<"Singleton constructor"<<endl;}

    ~Singleton() {}

};

Singleton::Object_Creator Singleton::_object_creator;

执行我们会看到(在 main 函数中还未使用到该单例):

Object_Creator constructor

Singleton constructor
说明,此时在main函数之前就初始化了单例对象。

对于前面的ASingleton 和 BSingleton 的样例,改进例如以下:

class ASingleton

{

public:

    static ASingleton* getInstance()

    {

        static ASingleton instance;

        return &instance;

    }

    void do_something()

    {

        cout<<"ASingleton do_something!"<<endl;

    }

protected:

    struct Object_Creator

    {

        Object_Creator()

        {

            ASingleton::getInstance();

        }

    };

    static Object_Creator _object_creator;



    ASingleton();

    ~ASingleton() {}

};





class BSingleton

{

public:

    static BSingleton* getInstance()

    {

        static BSingleton instance;

        return &instance;

    }

    void do_something()

    {

        cout<<"BSingleton do_something!"<<endl;

    }

protected:

    struct Object_Creator

    {

        Object_Creator()

        {

            BSingleton::getInstance();

        }

    };

    static Object_Creator _object_creator;



    BSingleton();

    ~BSingleton() {}

};

ASingleton::Object_Creator ASingleton::_object_creator;

BSingleton::Object_Creator BSingleton::_object_creator;



ASingleton::ASingleton()

{

    cout<<"ASingleton constructor!"<<endl;

    BSingleton::getInstance()->do_something();

}

BSingleton::BSingleton()

{

    cout<<"BSingleton constructor!"<<endl;

}

    这样程序就避免了 ASingleton 和 BSingleton 单例对象的初始化顺序问题,使得输出结果就始终是:

ASingleton constructor!

BSingleton constructor!

BSingleton do_something!

最后,展示一下加入了模板的实现:

template <typename T>

class Singleton

{

    struct object_creator

    {

        object_creator()

        {

            Singleton<T>::instance();

        }

        inline void do_nothing() const {}

    };



    static object_creator create_object;



public:

    typedef T object_type;

    static T& instance()

    {

        static T obj;

        //这个do_nothing是确保create_object构造函数被调用

        //这跟模板的编译有关

        create_object.do_nothing();

        return obj;

    }



};

template <typename T> typename Singleton<T>::object_creator
Singleton<T>::create_object;



class QMManager

{

protected:

    QMManager() {}

    ~QMManager() {}

    friend class Singleton<QMManager>;

public:

    void do_something() {};

};



int main()

{

    Singleton<QMManager>::instance().do_something();

    ;

}

   
boost 通过加入一个类似 proxy-class 的方式,实现了单例模式,可是显然添加了复杂性,在实际应用中应该依据实际情况採用适当的实现方案。





探测器 C++ Singleton(辛格尔顿)的更多相关文章

  1. 亮点面试题&amp;&amp;实现Singleton(辛格尔顿)模式-JAVA版本

    称号:设计一个类.我们只能产生这个类的一个实例.(来自<剑指Offer>) 解析:仅仅能生产一个实例的类是实现Singleton(单例)模式的类型.因为设计模式在面向对象程序设计中起着举足 ...

  2. 设计模式——辛格尔顿(Singleton)

    要想正确理解设计模式,首先必须明白它是为了解决什么问题而提出来的. 设计模式学习笔记 --Shulin 转载请注明出处:http://blog.csdn.net/zhshulin 单例模式属于设计模式 ...

  3. Swift辛格尔顿设计模式(SINGLETON)

    本文已更新为2.0语法,具体查看:一叶单例模式 一.意图 保证一个类公有一个实例.并提供一个訪问它的全局訪问点. 二.使用场景 1.使用场景 当类仅仅能有一个实例并且客户能够从一个众所周知的訪问点訪问 ...

  4. 【从cocos2d-x学习设计模式】第一阶段:辛格尔顿

    设计模式,它总结了前辈在许多方案重用代码.它是一个想法. 因为我们爱cocos2d-x,然后我们从去cocos2d-x在设计模式中,右一起学习!本篇解释未来辛格尔顿. 提cocos2d-x中间Dire ...

  5. 辛格尔顿和Android

    辛格尔顿(Singleton) .singleton.h,定义类的基本成员及接口 #ifndef SINGLETON_H_INCLUDE #define SINGLETON_H_INCLUDE cla ...

  6. 23种设计模式--单例模式-Singleton

    一.单例模式的介绍 单例模式简单说就是掌握系统的至高点,在程序中只实例化一次,这样就是单例模式,在系统比如说你是该系统的登录的第多少人,还有数据库的连接池等地方会使用,单例模式是最简单,最常用的模式之 ...

  7. 设计模式之单例模式(Singleton)

    设计模式之单例模式(Singleton) 设计模式是前辈的一些经验总结之后的精髓,学习设计模式可以针对不同的问题给出更加优雅的解答 单例模式可分为俩种:懒汉模式和饿汉模式.俩种模式分别有不同的优势和缺 ...

  8. PHP设计模式(四)单例模式(Singleton For PHP)

    今天讲单例设计模式,这种设计模式和工厂模式一样,用的非常非常多,同时单例模式比较容易的一种设计模式. 一.什么是单例设计模式 单例模式,也叫单子模式,是一种常用的软件设计模式.在应用这个模式时,单例对 ...

  9. The Java Enum: A Singleton Pattern [reproduced]

    The singleton pattern restricts the instantiation of a class to one object. In Java, to enforce this ...

随机推荐

  1. Eclipse+Maven创建webapp项目<二> (转)

    Eclipse+Maven创建webapp项目<二> 1.开启eclipse,右键new——>other,如下图找到maven project 2.选择maven project,显 ...

  2. POJ 2417 Discrete Logging 离散对数

    链接:http://poj.org/problem?id=2417 题意: 思路:求离散对数,Baby Step Giant Step算法基本应用. 下面转载自:AekdyCoin [普通Baby S ...

  3. android-adb通用

  4. 栈实现java

    栈是一种“先去后出”的抽象的数据结构.例如:我们在洗盘子的时候,洗完一个盘子,将其放在一摞盘子的最上面,但我们全部洗完后,要是有盘子时,我们会先从最上面的盘子开始使用,这种例子就像栈的数据结构一样,先 ...

  5. 玩转Web之Json(四)---json与(Object/List/Map)的相互转化

    在做web应用时,经常需要将json转化成Object/list/map或者将Object/List/map转化成json,通过简单封装可以在写代码是减轻很多负担.本文将给出json转化的一系列方法. ...

  6. C语言中的函数指针

    C语言中的函数指针 函数指针的概念:   函数指针是一个指向位于代码段的函数代码的指针. 函数指针的使用: #include<stdio.h> typedef struct (*fun_t ...

  7. iOS:编译错误 linker command failed with exit code 1 (use -v to see invocation)

    将project不加入.m要求加入 watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvdTAxMzI0MzQ2OQ==/font/5a6L5L2T/fontsi ...

  8. 做一个自动修改本机IP和mac的bat文件

    原文:做一个自动修改本机IP和mac的bat文件 1.ip bat修改理论探讨 前两天我突然萌生了一个念头:能不能做一个小程序来实现自动配置或修改IP和mac,达到一键搞定的目的,这样尤其适合那些带着 ...

  9. IOS上传文件开发

    IOS上传文件开发     在移动应用开发  文件形式上传是不可缺少的,近期把IOS这块文件上传文件代码简单的整理一下.假设大家有须要安卓这边的代码,本人也能够分享给大家! QQ群:74432915 ...

  10. 【干货】免费获得WebStorm软件

    内容提要: 1.WebStorm简介 2.如何免费获得WebStorm 3.利用学生身份免费获得正式版WebStorm WebStorm简介 WebStorm 是一款前端开发 IDE(集成开发环境), ...