美文网首页
【剑指offer】面试题2:实现Singleton模式

【剑指offer】面试题2:实现Singleton模式

作者: 不会code的程序猿 | 来源:发表于2017-06-22 13:24 被阅读75次

    参考:http://blog.csdn.net/huhaijing/article/details/51756225

    题目:

    设计一个类,我们只能实现该类的一个实例。
    单例模式:保证一个类只有一个实例,并提供一个访问它的全局访问点。
    首先,需要保证一个类只有一个实例,在类中,要构造一个实例,就必须调用类的构造函数,如此,为了防止在外部调用类的构造函数而构造实例,需要将构造函数的访问权限标记为protected或private;最后,需要提供给全局访问点,就需要在类中定义一个static函数,返回在类内部唯一构造的实例。

    解法1:只适用于单线程环境

    // 剑指offer 面试题2 实现Singleton模式
    #include <iostream>
    using namespace std;
    
    class Singleton
    {
    public:
        static Singleton* getInstance()
        {
            // 在后面的Singleton实例初始化时,若后面是new Singleton(),则此处不必new;(废话)
            // 若后面是赋值成NULL,则此处需要判断,需要时new
            // 注意!然而这两种方式并不等价!后面的Singleton实例初始化时,new Singleton(),其实是线程安全的,因为static初始化是在主函数main()之前,那么后面的方法岂不是很麻烦。。。。这也是我测试的时候想到的
            /*
            if(m_pInstance == NULL)
            {
            m_pInstance = new Singleton();
            }
            */
            return m_pInstance;
        }
    
        static void destroyInstance()
        {
            if (m_pInstance != NULL)
            {
                delete m_pInstance;
                m_pInstance = NULL;
            }
        }
    
    private:
        Singleton(){}
        static Singleton* m_pInstance;
    };
    
    // Singleton实例初始化
    Singleton* Singleton::m_pInstance = new Singleton(); // 前面不能加static,会和类外全局static混淆
    
    // 单线程获取多次实例
    void Test1(){
        // 预期结果:两个实例指针指向的地址相同
        Singleton* singletonObj = Singleton::getInstance();
        cout << singletonObj << endl;
    
        Singleton* singletonObj2 = Singleton::getInstance();
        cout << singletonObj2 << endl;
    
        Singleton::destroyInstance();
    }
    
    int main(){
        Test1();
        getchar();
        return 0;
    }
    

    解法2:多线程可运行但效率低下

    如果在同一时刻两个线程同时运行到if (m_Instance != NULL),此刻又没有创建实例,那么两个线程都会创建一个实例。为了保证在多线程环境下只生成一个实例,需要加上一个同步锁。

    #include <iostream>
    #include <mutex>
    #include <thread>
    #include <vector>
    using namespace std;
    
    class Singleton
    {
    private:
        static mutex m_mutex; // 互斥量
    
        Singleton(){}
        static Singleton* m_pInstance;
    
    public:
        static Singleton* getInstance(){
            if (m_pInstance == NULL){
                m_mutex.lock(); // 使用C++11中的多线程库
                if (m_pInstance == NULL){ // 两次判断是否为NULL的双重检查
                    m_pInstance = new Singleton();
                }
                m_mutex.unlock();
            }
            return m_pInstance;
        }
    
        static void destroyInstance(){
            if (m_pInstance != NULL){
                delete m_pInstance;
                m_pInstance = NULL;
            }
        }
    };
    
    Singleton* Singleton::m_pInstance = NULL; // 所以说直接new 多好啊,可以省去Lock/Unlock的时间
    mutex Singleton::m_mutex;
    
    
    void print_singleton_instance(){
        Singleton *singletonObj = Singleton::getInstance();
        cout << singletonObj << endl;
    }
    
    // 多个进程获得单例
    void Test1(){
        // 预期结果,打印出相同的地址,之间可能缺失换行符,也属正常现象
        vector<thread> threads;
        for (int i = 0; i < 10; ++i){
            threads.push_back(thread(print_singleton_instance));
        }
    
        for (auto& thr : threads){
            thr.join();
        }
    }
    
    int main(){
        Test1();
        Singleton::destroyInstance();
        getchar();
        return 0;
    }
    

    此处进行了两次m_pInstance == NULL的判断,是借鉴了Java的单例模式实现时,使用的所谓的“双检锁”机制。因为进行一次加锁和解锁是需要付出对应的代价的,而进行两次判断,就可以避免多次加锁与解锁操作,同时也保证了线程安全。但是,如果进行大数据的操作,加锁操作将成为一个性能的瓶颈;为此,一种新的单例模式的实现也就出现了。如果不进行两次判断,则每次调用static Singleton* getInstance()都需要先加锁,然后再判断,这样效率更加低下。

    解法3:const static 型实例

    #include <iostream>
    #include <thread>
    #include <vector>
    using namespace std;
    
    class Singleton
    {
    private:
        Singleton(){}
        static const Singleton* m_pInstance; //这里和之前的不同
    public:
        static Singleton* getInstance(){
    
            return const_cast<Singleton *>(m_pInstance); // 去掉“const”特性
            // 注意!若该函数的返回值改为const static型,则此处不必进行const_cast静态转换
            // 所以该函数可以改为:
            /*
            const static Singleton* getInstance(){
                return m_pInstance;
            }
            */
        }
    
        static void destroyInstance(){
            if(m_pInstance != NULL){
                delete m_pInstance;
                m_pInstance = NULL;
            }
        }
    };
    const Singleton* Singleton::m_pInstance = new Singleton(); // 利用const只能定义一次,不能再次修改的特性,static继续保持类内只有一个实例
    
    void print_singleton_instance(){
        Singleton *singletonObj = Singleton::getInstance();
        cout << singletonObj << endl;
    }
    
    // 多个进程获得单例
    void Test1(){
        // 预期结果,打印出相同的地址,之间可能缺失换行符,也属正常现象
        vector<thread> threads;
        for(int i = 0; i < 10; ++i){
            threads.push_back(thread(print_singleton_instance));
        }
    
        for(auto& thr : threads){
            thr.join();
        }
    }
    
    int main(){
        Test1();
        Singleton::destroyInstance();
        return 0;
    }
    

    解法4:实现按需创建实例

    #include <iostream>
    #include <thread>
    #include <vector>
    using namespace std;
    
    class Singleton
    {
    private:
        Singleton(){}
    
    public:
        static Singleton* getInstance(){
            static Singleton m_pInstance; // 注意,声明在该函数内
            return &m_pInstance;
        }
    };
    
    void print_singleton_instance(){
        Singleton *singletonObj = Singleton::getInstance();
        cout << singletonObj << endl;
    }
    
    // 多个进程获得单例
    void Test1(){
        // 预期结果,打印出相同的地址,之间可能缺失换行符,也属正常现象
        vector<thread> threads;
        for(int i = 0; i < 10; ++i){
            threads.push_back(thread(print_singleton_instance));
        }
    
        for(auto& thr : threads){
            thr.join();
        }
    }
    
    // 单个进程获得多次实例
    void Test2(){
        // 预期结果,打印出相同的地址,之间换行符分隔
        print_singleton_instance();
        print_singleton_instance();
    }
    
    int main(){
        cout << "Test1 begins: " << endl;
        Test1();
        cout << "Test2 begins: " << endl;
        Test2();
        return 0;
    }
    

    其实自己还有好多地方没太明白,先写在这里吧。

    相关文章

      网友评论

          本文标题:【剑指offer】面试题2:实现Singleton模式

          本文链接:https://www.haomeiwen.com/subject/ueaxcxtx.html