C++ 单例模式(泛型, 线程安全)


本文相关代码已上传: 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;
}

结果输出如下:
在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值