我个人整理的C++单例模式,推荐boost方式(★firecat推荐★)

1,861 阅读4分钟

请重点关注三和四,boost库实现的C++单例模板类。

 

一、简单实现(不推荐)

// C++单例模式测试程序
//


#include "stdafx.h"
#include <iostream>


using namespace std;


class CSingleton
{
private:
CSingleton(){;}//单例模式,禁止外部实例化


public:
//获取静态对象指针
static CSingleton *getInstance()
{ if (NULL == s_pInstance)
{
std::cout<<"new\n";
s_pInstance = new CSingleton();
}
return s_pInstance;
}


private:
static CSingleton *s_pInstance;
//类CFree被定义为CSingleton的私有内嵌类,以防该类被在其他地方滥用。它的唯一作用就是在析构函数中删除CSingleton的实例。
//程序运行结束时,系统会调用CSingleton的静态成员CFree的析构函数,该析构函数会删除单例的唯一实例。
class CFree
{
public:
~CFree()
{
if (CSingleton::s_pInstance != NULL)
{
std::cout<<"delete\n";
delete CSingleton::s_pInstance;
CSingleton::s_pInstance = NULL;
}
}
};
//定义一个静态成员变量,程序结束时,系统会自动调用它的析构函数
static CFree s_Fe;
};


CSingleton* CSingleton::s_pInstance = NULL;
CSingleton::CFree CSingleton::s_Fe;//类里面的静态变量要在类外部定义,如果没有这句话,程序结束时就不会运行CFree的析构函数.


int _tmain(int argc, _TCHAR* argv[])
{
CSingleton::getInstance();
return 0;
}

以上方法是可以编译正常运行的,析构函数也能如期执行。缺点是:1、线程不安全,多线程情况下会重复调用初始化函数;2、没有使用模板,数据类型不通用。所以不推荐使用。

 

二、模板实现1(不推荐)

我想采用C++模板的方式,重新改写了一下:

Singleton.h文件如下

#pragma once

template<typename T>
class CSingleton
{
public:
	static T& getInstance()
	{
		if (NULL == s_pInstance)
		{
			std::cout << "new\n";
			s_pInstance = new T();
		}

		return *s_pInstance;
	}

private:
	CSingleton();//单例模式,禁止外部实例化
	~CSingleton();
	CSingleton(const CSingleton&);
	CSingleton& operator=(const CSingleton&);

private:
	static T* s_pInstance;
	//类CFree被定义为CSingleton的私有内嵌类,以防该类被在其他地方滥用。它的唯一作用就是在析构函数中删除CSingleton的实例。  
	//程序运行结束时,系统会调用CSingleton的静态成员CFree的析构函数,该析构函数会删除单例的唯一实例。  
	class CFree
	{
	public:
		~CFree()
		{
			if (T* CSingleton<T>::s_pInstance != NULL)
			{
				std::cout << "delete\n";
				delete T* CSingleton<T>::s_pInstance;
				T* CSingleton<T>::s_pInstance = NULL;
			}
		}
	};
	//定义一个静态成员变量,程序结束时,系统会自动调用它的析构函数  
	static CFree s_Fe;
};

template<typename T> T* CSingleton<T>::s_pInstance = NULL;
template <typename T> typename CSingleton<T>::CFree CSingleton<T>::s_Fe;

 

main.cpp文件如下

#include <iostream>  
#include "Singleton.h"

class  A
{
public:
	 A();
	~ A();

public:
	void init()
	{
		std::cout << "hello world\n";
	}
};

 A:: A()
{
}

 A::~ A()
{
}

int main()
{
	CSingleton<A>::getInstance().init();

    return 0;
}

结论是,这样子编译可以正常通过。但是有个问题是单例模板的内部类,析构函数没有执行。原因未知,这是个遗留问题,待解决!目前只能通过手动释放内存来解决。

static void destroy()
{
	delete s_pInstance;
}

 

三、模板实现2(可行)

//C++设计模式——一个基于C++11的单例模板类
//https://blog.csdn.net/godmaycry/article/details/78458329

#include <memory>
#include <mutex>

template <typename T>
class CSingleton
{
public:
    //获取全局单例对象
    template <typename... Args>
    static std::shared_ptr<T> GetInstance(Args &&... args)
    {
        if (!s_pSelf)
        {
            std::lock_guard<std::mutex> lock(s_mutex);
            if (nullptr == s_pSelf)
            {
                s_pSelf = std::make_shared<T>(std::forward<Args>(args)...);
            }
        }

        return s_pSelf;
    }

    //主动析构单例对象(一般不需要在外部主动调用该函数,除非特殊需求)
    static void DesInstance()
    {
        if (s_pSelf)
        {
            s_pSelf.reset();
            s_pSelf = nullptr;
        }
    }

private:
    explicit CSingleton() {}
    ~CSingleton() {}
    //禁止拷贝
    CSingleton(const CSingleton &) = delete;
    CSingleton &operator=(const CSingleton &) = delete;
    CSingleton(CSingleton &&) = delete;
    CSingleton &operator=(CSingleton &&) = delete;

private:
    static std::shared_ptr<T> s_pSelf;
    static std::mutex s_mutex;
};

template <typename T>
std::shared_ptr<T> CSingleton<T>::s_pSelf = nullptr;

template <typename T>
std::mutex CSingleton<T>::s_mutex;

使用举例说明:

#include "singleton.h"

CSingleton<CToolBox>::GetInstance()->myfun();

 

四、来自boost C++库的实现方式(推荐)

boost库实现线程安全,避免重复初始化 --- 推荐使用

boost库有多张单例的实现,分散在不同的代码里,能够独立摘出来用的主要有以下四个:

  • boost/container/detail/singleton.hpp
  • boost/serialization/singleton.hpp
  • boost/thread/detail/singleton.hpp
  • boost/pool/singleton_pool.hpp

尤其以前两个为主。我这里只讲解第一个,\boost_1_69_0\boost\container\detail\singleton.hpp原生的代码是这样的:

// boost/container/detail/singleton.hpp
// T must be: no-throw default constructible and no-throw destructible
template <typename T>
struct singleton_default
{
  private:
    struct object_creator
    {
      // This constructor does nothing more than ensure that instance()
      //  is called before main() begins, thus creating the static
      //  T object before multithreading race issues can come up.
      object_creator() { singleton_default<T>::instance(); }
      inline void do_nothing() const { }
    };
    static object_creator create_object;

    singleton_default();

  public:
    typedef T object_type;

    // If, at any point (in user code), singleton_default<T>::instance()
    //  is called, then the following function is instantiated.
    static object_type & instance()
    {
      // This is the object that we return a reference to.
      // It is guaranteed to be created before main() begins because of
      //  the next line.
      static object_type obj;

      // The following line does nothing else than force the instantiation
      //  of singleton_default<T>::create_object, whose constructor is
      //  called before main() begins.
      create_object.do_nothing();

      return obj;
    }
};
template <typename T>
typename singleton_default<T>::object_creator
singleton_default<T>::create_object;

 

下面做一下中文讲解,出于习惯,我把:struct singleton_default名字修改成了class CSingleton。

其实注释已经说得很清楚了,这个实现使用了一个struct object_creator来作为类的static成员变量,单例的实体是obj,并不暴露出来,只是作为类的成员函数的static变量(local static对象)。通过create_object在main之前被执行构造来保证单例是在main之前被构造好。这个单例是利用main调用之前,程序只有一个线程,来保证单例在多线程下的唯一性。

#pragma once

template <typename T>
class CSingleton
{
private:
	struct object_creator
	{
		object_creator()
		{
			CSingleton<T>::instance();
		}

		inline void do_nothing()const {}
	};

	//利用类的静态对象object_creator的构造初始化,在进入main之前已经调用了instance,
	//从而避免了多次初始化的问题
	static object_creator create_object;

public:
	typedef T object_type;
	static object_type& instance()
	{
		static object_type obj;
		//据说这个do_nothing是确保create_object构造函数被调用
		//这跟模板的编译有关
		//do_nothing 是必要的,do_nothing的作用有点意思,  
		//如果不加create_object_.do_nothing();这句话,在main函数前面,create_object_的构造函数都不会被调用,instance当然也不会被调用,  
		//我的估计是模版的延迟实现的特效导致,如果没有这句话,编译器也不会实现Singleton<T>::object_creator,所以就会导致这个问题  
		create_object.do_nothing();
		return obj;
	}

private:
	CSingleton();//单例模式,禁止外部实例化  
	~CSingleton();
	CSingleton(const CSingleton&);
	CSingleton& operator=(const CSingleton&);
};

//因为create_object是类的静态变量,必须有一个通用的声明
template <typename T> typename CSingleton<T>::object_creator CSingleton<T>::create_object;

 

使用举例说明:main.cpp

#include "stdafx.h"
#include <iostream>
#include "singleton.h"

class QMManager
{
	//友元类的所有成员函数都是另一个类的友元函数,都可以访问另一个类中的隐藏信息(包括私有成员和保护成员),
	//当希望一个类可以存取另一个类的私有成员时,可以将该类声明为另一类的友元类.
	friend class CSingleton<QMManager>; 
	
protected:
	QMManager() {};//这里必须写{},否则会编译出错.error LNK2019: 无法解析的外部符号 "protected: __thiscall QMManager::QMManager(void)" (??0QMManager@@IAE@XZ)
	~QMManager() {};
	
public:
	void do_something()
	{
		std::cout << "hello world";
	};
};

int main()
{
	CSingleton<QMManager>::instance().do_something();
    return 0;
}

 

注意事项

所谓的单例仅仅是针对单个exe或者单个dll文件而言。如果某个单例类同时被exe和dll调用,要注意,exe和dll会各自实例化这个单例类。它们是各自独立的。所以需要共享的数据应该定义为静态成员变量。

 

参考文献

www.jianshu.com/p/333d4b37d…  boost中的单例模式(singleton)

coolshell.cn/articles/26…  深入浅出单实例SINGLETON设计模式

blog.csdn.net/libaineu200…