📌  相关文章
📜  教资会网络 | UGC NET CS 2016 年 7 月 – III |问题 30(1)

📅  最后修改于: 2023-12-03 15:26:03.736000             🧑  作者: Mango

教资会网络 | UGC NET CS 2016 年 7 月 – III |问题 30

本题涉及如何使用C++编写一个线程安全的单例模式(Singleton Pattern)类。

什么是单例模式?

单例模式是一种设计模式,它保证一个类有且仅有一个实例,并且提供一个全局的访问点。

如何实现一个线程安全的单例模式?
懒汉式

懒汉式是一种在使用时才会创建实例的单例模式。在懒汉式实现中,需要注意线程安全问题。

class Singleton {
public:
    static Singleton& Instance() {
        static Singleton instance;
        return instance;
    }

private:
    Singleton() {};                  // 构造函数私有化
    Singleton(const Singleton&);     // 拷贝构造函数私有化
    Singleton& operator=(const Singleton&);  // 赋值运算符私有化
};

在懒汉式实现中,使用一个静态局部变量来保存单例实例,确保只有在使用时才会创建实例。由于静态局部变量只会在第一次使用时初始化,因此可以保证单例实例只有一个。然而,这种实现方式并不是线程安全的。当多个线程同时访问该类时,在第一个线程创建实例期间,其他线程也会判断出instance为空,然后各自创建实例,导致多个实例的出现。

为了保证线程安全,可以在静态局部变量初始化时加锁。在C++11中,可以用std::call_once来实现这一操作。

#include <mutex>

class Singleton {
public:
    static Singleton& Instance() {
        std::call_once(once_flag_, &Singleton::init);
        return *instance_;
    }

private:
    Singleton() {};                  
    Singleton(const Singleton&);     
    Singleton& operator=(const Singleton&);
    
    static void init() {
        instance_ = new Singleton();
        atexit(destroy);
    }
    
    static void destroy() {
        if (instance_ != nullptr) {
            delete instance_;
            instance_ = nullptr;
        }
    }
    
    static Singleton* instance_;
    static std::once_flag once_flag_;
};

在这种实现方式中,once_flag_是std::once_flag类型的全局变量,它保证init函数只会被调用一次。在init函数中,通过new创建对象,并通过atexit在程序结束时自动销毁单例实例(仅适用于单线程环境)。需要注意的是,这种实现方式并没有释放once_flag_和instance_的内存,因此需要在程序结束时手动释放。

饿汉式

饿汉式是一种在程序启动时就创建实例的单例模式。在饿汉式实现中,由于实例是在程序启动时创建的,在使用时不会有线程安全问题。

class Singleton {
public:
    static Singleton& Instance() {
        return *instance_;
    }

private:
    Singleton() {};                  
    Singleton(const Singleton&);     
    Singleton& operator=(const Singleton&);
    
    static Singleton* instance_;
};

Singleton* Singleton::instance_ = new Singleton();

这种实现方式与懒汉式相比较简单,但有一个缺点:由于实例是在程序启动时创建的,对于经常不会用到该实例的程序而言,它是一种资源浪费。