本文相关代码已上传: https://github.com/pengguoqing/samples_code/tree/master/c%2B%2B/singleton
一、前言
单例模式保证了类的实例在整个 PE 文件(比如在 dll 动态库和 exe 进程)中只存在一份, 这个模式是蛮有用的。比如写日志的实例通常只创建一次, 然后提供一个全局的访问接口, 各个流程根据需要调用接口写日志。
单例模式主要分为饿汉和懒汉两个版本, 个人比较喜欢资源需要被使用的时候再创建这个原则, 所以本文实现的是懒汉版本。目前网上有许多的单例模式实现版本, 但是要么不支持线程安全的初始化, 要么支持泛型但是由于实例类的构造参数个数和参数类型可能都不同,不容易做一个所有类型都通用的单例, 所以本文实现一个初始化线程安全并且支持可变模板参数的单例。
二、实现
2.1、线程安全的初始化
单例类的初始化需要注意两个方面:①线程安全; ②内存的 reorder; ③可变模板参数。线程安全使用 std::lock_guard , 避免内存 reorder 使用 std::atomic, 可变模板参数再借助转发引用和完美转发保留左右值属性。 代码如下:
template<class... Args>
static T* InitInstance(Args&&... args)
{
T* ins = m_instance.load(std::memory_order_relaxed);
std::atomic_thread_fence(std::memory_order_acquire);
if (nullptr == ins)
{
std::lock_guard<std::mutex> w_lock(m_mutex);
ins = m_instance.load(std::memory_order_relaxed);
if (nullptr == ins) {
ins = new T(std::forward<Args>(args)...);
std::atomic_thread_fence(std::memory_order_release);
m_instance.store(ins, std::memory_order_relaxed);
}
}
return ins;
}
2.2、访问接口和资源释放
代码如下:
static T* GetInstance()
{
return m_instance.load(std::memory_order_relaxed);
}
static void DestoryInstance()
{
delete m_instance.load(std::memory_order_relaxed);
m_instance.store(nullptr, std::memory_order_relaxed);
}
2.3、其他
因为 static 成员函数的性质, 所以实例指针和线程同步锁都需要声明为 static。另外单例类不对外提供构造和拷贝构造, 并且不能赋值,代码如下:
private:
CXSingleton() = default;
~CXSingleton() = default;
CXSingleton(const CXSingleton& another) = delete;
CXSingleton& operator=(const CXSingleton& another) = delete;
private:
static std::atomic<T*> m_instance;
static std::mutex m_mutex;
三、测试
主要观察可变模板参数和完美转发的功能,线程安全部分是借鉴的 Modernes C++ Mentoring 作者的实现,就偷懒不测试了, 哈哈哈。 代码如下:
struct A
{
A(int data, std::string str)
:m_data(data),
m_str(str)
{
std::cout<<"A construct"<<std::endl;
};
~A()
{
std::cout << "A deconstruct" << std::endl;
}
void Dump()
{
std::cout<<"A-data: "<< m_data <<" A-str: " << m_str << std::endl;
}
int m_data;
std::string m_str;
};
struct B
{
B(std::string&& rvalue)
{
std::cout<<"B construct"<< std::endl;
};
~B()
{
std::cout << "B deconstruct" << std::endl;
};
void Print()
{
std::cout << "dummy class B" << std::endl;
}
};
int main(int argc, char*argv[])
{
int data = 100;
A* ptr = nullptr;
std::string str ="Hello world!";
ptr = CXSingleton<A>::InitInstance(data, str);
CXSingleton<A>::GetInstance()->Dump();
CXSingleton<A>::DestoryInstance();
ptr = CXSingleton<A>::GetInstance();
if (nullptr == ptr)
{
std::cout<<"A instance destroy"<<std::endl<<"------------"<<std::endl;
}
CXSingleton<B>::InitInstance(std::move(str));
CXSingleton<B>::GetInstance()->Print();
CXSingleton<B>::DestoryInstance();
return 0;
}
结果输出如下: