C++单例模式及实例
单例模式是非常常用的设计模式,通俗讲就是保证全局仅存在一份类型实例。
我们通过模板类来实现泛型单例模式,所有单例类对象存储在static变量中,全局静态变量要早于main方法之前初始化,且只初始化一次,因此只要调用类似HungrySingleton<log>::GetInstance()
这样的语句即可获取单例对象log
的指针。
对于C++来说,需要保证单例类HungrySingleton
和LazySingleton
构造函数、析构函数、拷贝和赋值通通不可用,因此其继承了一个删除了上述方法的父类,从而禁止二者调用。
饿汉式比较容易处理,只需要在类体外进行相应的定义即进行了初始化。由于static对象只进行一次初始化,因此饿汉式自身保证了线程安全性。
懒汉式需要考虑线程安全的问题,当真正用到时才需要进行实例化,这个实例化过程需要保证线程安全。方法是在检测到对象不存在时加锁,并再一次检测,如果真的不存在,则进行实例化。确保实例化后可以从静态对象中安全取出单例对象。
**已废弃方法:**当然,不要忘了delete
,我们可以在static初始化时创建一个Garbage
类,其负责在单例对象生命期结束时,回收相应资源。
2/18日修改:上述通过调用另一个静态Garbage对象来删除Instance在实测中无法调用析构函数,改为使用智能指针来正确释放
9/20:在字节实习几个月后来后在看看自己写的什么狗屁代码…静态变量本身是一种特殊变量,由编译器在进入main之前创建和初始化,在离开main之后由编译器释放,所以何必用智能指针包装…其实公司项目中最常用局部变量的那种单例,编译器保证了局部变量的初始化时是线程安全的。
/**
* @file singleton.h
* @author huang (shaobohuang.1998@gmail.com)
* @brief 简单的singleton实例
* @version 0.1
* @date 2022-02-17
*
* @copyright Copyright (c) 2022
*
*/
#include <pthread.h>
#include <iostream>
#include <memory>
/**
* @brief 不允许单例类调用构造、析构、复制和赋值,仅能通过静态方法获取实例
*
*/
class NonInstantiatable {
public:
NonInstantiatable() = delete;
~NonInstantiatable() = delete;
NonInstantiatable(const NonInstantiatable&) = delete;
NonInstantiatable& operator=(const NonInstantiatable&) = delete;
};
template<typename T>
class HungrySingleton : public NonInstantiatable{
// 饿汉式
public:
static std::shared_ptr<T> GetInstance();
private:
static std::shared_ptr<T> Instance;
};
template<typename T>
std::shared_ptr<T> HungrySingleton<T>::Instance(new T);
template<typename T>
std::shared_ptr<T> HungrySingleton<T>::GetInstance() {
return Instance;
}
class MutexImpl {
public:
MutexImpl() {
std::cout<< "mutex created" << std::endl;
pthread_mutex_init(&m_mutex, nullptr);
}
void Lock() {
pthread_mutex_lock(&m_mutex);
}
void Unlock() {
pthread_mutex_unlock(&m_mutex);
}
~MutexImpl() {
std::cout<< "mutex cleared" << std::endl;
pthread_mutex_destroy(&m_mutex);
}
private:
pthread_mutex_t m_mutex;
};
template<typename T>
class LazySingleton : public NonInstantiatable{
// 懒汉式
public:
static std::shared_ptr<T> GetInstance();
private:
static std::shared_ptr<T> Instance;
static std::unique_ptr<MutexImpl> Mutex;
};
template<typename T>
std::shared_ptr<T> LazySingleton<T>::Instance(nullptr);
template<typename T>
std::unique_ptr<MutexImpl> LazySingleton<T>::Mutex(new MutexImpl);
template<typename T>
std::shared_ptr<T> LazySingleton<T>::GetInstance() {
if(!Instance) {
Mutex->Lock();
if(!Instance) {
LazySingleton<T>::Instance.reset(new T);
}
Mutex->Unlock();
}
return Instance;
}
template<typename T>
class Singleton : public NonInstantiatable{
// 基于局部静态对象的懒汉式
public:
static T* GetInstance() {
static T v;
return &v;
}
};
/**
* @file singleton_main.cc
* @author your name (you@domain.com)
* @brief main方法
* @version 0.1
* @date 2022-02-17
*
* @copyright Copyright (c) 2022
*
*/
/**
* @file singleton_main.cc
* @author your name (you@domain.com)
* @brief main方法
* @version 0.1
* @date 2022-02-17
*
* @copyright Copyright (c) 2022
*
*/
#define NUM 20
#include "singleton.h"
#include <iostream>
#include <unistd.h> // for syscall()
#include <sys/syscall.h> // for SYS_xxx definitions
class Log {
public:
Log() {
std::cout << "Log Initialed" << std::endl;
}
~Log() {
std::cout << "Log cleared" << std::endl;
}
};
void* test(void* data) {
auto log = HungrySingleton<Log>::GetInstance();
std::cout << "pthread:" << syscall(SYS_gettid) << "log address" << log.get() << std::endl;
return NULL;
}
int main(int argc, char** argv) {
//auto log1 = HungrySingleton<Log>::GetInstance();
//auto log2 = HungrySingleton<Log>::GetInstance();
//auto log1 = LazySingleton<Log>::GetInstance();
//auto log2 = LazySingleton<Log>::GetInstance();
/*if(log1 == log2) {
std::cout << "equal" << std::endl;
}*/
pthread_t t[NUM];
for(int i = 0; i < NUM; ++i) {
pthread_create(&t[0] + i, 0, test, NULL);
}
for(int i = 0; i < NUM; ++i) {
pthread_join(t[i], NULL);
}
return 0;
}