C++单例模式-懒汉与饿汉

时间:2025-03-25 21:09:41

一、懒汉式

(一)线程不安全的懒汉式
特点: 时间换空间
懒汉:在第一次用到类实例的时候,实例化对象。
(a)构造函数声明为private或protect防止被外部函数实例化。
(b)内部保存一个private static的指针ptr,保存全局唯一的实例对象。
(c)实例化的动作由一个public的方法代劳,该方法返回单例类唯一的实例对象。
问题: 线程不安全。
当两个线程同时首次调用Instance方法,
并且同时检测到ptr是nullptr值,则两个线程会同时构造一个实例给ptr。

class Singleton {
public:
	static Singleton* Instance() {
		if (ptr == nullptr) {
			ptr = new Singleton;
		}
		return ptr;
	}
private:
	static Singleton* ptr;
private:
    Singleton() = default;
    Singleton(const Singleton&) = delete;
    Singleton& operator=(const Singleton&) = delete;
};
Singleton* Singleton::ptr = nullptr;

(二)线程不安全的懒汉式–加锁
方法: Double-Checked Locking Pattern (DCLP)。
问题:
1.线程不安全。
第一次check没有加锁,在多线程情况下,存在线程不安全问题。
2.资源需要手动释放
需要手动调用Destroy(),释放单例模式管理的内部内存资源。

class Singleton {
public:
	static Singleton* Instance() {
		if (ptr == nullptr) {
	        pthread_mutex_lock(&mutex);
			if (ptr == nullptr) {
				ptr = new Singleton;
			}
		    pthread_mutex_unlock(&mutex);
	     }
	     return ptr;
    }
    void Destroy() {
        if (ptr != nullptr) {
            delete ptr;
            ptr = nullptr;
        }
     }
private:
	static Singleton* ptr;
	static pthread_mutex_t mutex;
private:
    Singleton() = default;
    Singleton(const Singleton&) = delete;
    Singleton& operator=(const Singleton&) = delete;
};
Singleton* Singleton::ptr = nullptr;

(三)线程安全的懒汉式–局部静态变量
在Instance方法里定义一个静态实例,可以保证拥有唯一实例,
在返回时只需要返回其指针就可以了。
推荐这种实现方法。

class Singleton {
public:
	static Singleton* Instance()  {
	   static Singleton obj;
	   return &obj;
    }
private:
    Singleton() = default;
    Singleton(const Singleton&) = delete;
    Singleton& operator=(const Singleton&) = delete;
};

(四)线程安全的懒汉式–std::call_once
C++11提供一种方法,可以线程安全地只调用一次函数。
使用std::call_once和std::once_flag。
std::call_once是一种lazy load机制。

class Singleton {
public:
    virtual ~Singleton(void) = default;
    static std::shared_ptr<Singleton> Instance() {
        static std::once_flag onceflag;
        static std::shared_ptr<Singleton> ptr = nullptr;
        std::call_once(onceflag, [&]() {
            (new Singleton());
        });
        return ptr;
    }
private:
    Singleton() = default;
    Singleton(const Singleton&) = delete;
    Singleton& operator=(const Singleton&) = delete;
};

二、饿汉式

特点:空间换时间
饿汉:在单例类定义的时候就进行实例化对象。
(a)构造函数声明为private或protect防止被外部函数实例化。
(b)内部保存一个private static的指针ptr,保存全局唯一的实例对象。
(c)实例化的动作由一个public的方法代劳,该方法返回单例类唯一的实例对象。
(d)线程安全。

class Singleton {
public:
	static Singleton* Instance() {
	    return ptr;
    }
private:
	static Singleton* ptr;
private:
    Singleton() = default;
    Singleton(const Singleton&) = delete;
    Singleton& operator=(const Singleton&) = delete;
};
Singleton* Singleton::ptr = new Singleton;

特点与选择:
在访问量较小时,采用懒汉实现,这是以时间换空间。
在访问量比较大,采用饿汉实现,这是以空间换时间。