• C++ 单例模板


    懒汉式,使用时再实例化出单例对象

    首先重载默认构造函数,拷贝运算符与 = 运算符并设置为私有。因为Singleton不会被实例化,故可不实现默认构造函数

    template<typename MotionType>
    class Singleton
    {
    private:
    	Singleton();
    	Singleton(const Singleton&) = delete;
    	Singleton& operator=(const Singleton&) = delete;
    };
    

    接着声明pInstance,类型为MotionType* ,GetInstance方法,返回值为MotionType*

    private:
    	static MotionType* pInstance;
    public:
    	static MotionType* GetInstance();
    

    静态成员需要在类外进行初始化

    template<class MotionType>
    MotionType* Singleton<MotionType>::pInstance = nullptr;
    

    实现获取单例的静态方法,注意new的类型应为MotionType

    template<class MotionType>
    inline MotionType* Singleton<MotionType>::GetInstance()
    {
    	if (pInstance == nullptr)
    		pInstance = new MotionType();
    	return pInstance;
    }
    

    整体代码如下,书写在Singleton.hpp中

    template<class MotionType>
    class Singleton
    {
    private:
    	Singleton();
    	Singleton(const Singleton&) = delete;
    	Singleton& operator=(const Singleton&) = delete;
    	static MotionType* pInstance;
    public:
    	static MotionType* GetInstance();
    };
    template<class MotionType>
    MotionType* Singleton<MotionType>::pInstance = nullptr;
    
    template<class MotionType>
    inline MotionType* Singleton<MotionType>::GetInstance()
    {
    	if (pInstance == nullptr)
    		pInstance = new MotionType();
    	return pInstance;
    }
    

    除了Singleton模板类,还应实现GotoCave单例类,GotoCave类同样需要重载默认构造,拷贝构造以及 = 运算符并设置为private。需要注意的是GotoCave类中应将Singleton类设为友元,以让Singleton类能够调用到GotoCave类的构造函数

    class GotoCave
    {
    private:
    	friend class Singleton<GotoCave>;
    	GotoCave() {}
    	GotoCave(const GotoCave&) = delete;
    	GotoCave& operator=(const GotoCave&) = delete;
    public:
    	//Whatever you want..
    };
    

    实际使用中,当模板参数调用到别的头文件中的类时,需要在当前头文件中提前声明,否则编译器将报错 “ xxx:找不到提示 ”

    class GotoCave;
    //Too many codes..
    auto temp = Singleton<GotoCave>::GetInstance();
    

    但是,以上代码存在问题。pInstance作为模板类的成员变量,在程序结束的时候并不会调用GotoCave的析构函数,这样就会导致内存泄漏。在多线程的情况下,某个线程执行new的同时,另一个线程可能正在对if进行判断,而此时实例还未被创建出来,这并不是我们期望的结果。在Effective C++中提到了一种Meyers' Singleton的方法。该方法不仅能防止内存泄漏,还具有线程安全性。改进代码如下

    [^Magic Static]: If control enters the declaration concurrently while the variable is being initialized, the concurrent execution shall wait for completion of the initialization. 如果当变量在初始化的时候,并发同时进入声明语句,并发线程将会阻塞等待初始化结束。

    template<class MotionType>
    class Singleton
    {
    private:
    	Singleton();
    	Singleton(const Singleton&) = delete;
    	Singleton& operator=(const Singleton&) = delete;
    public:
    	static MotionType& GetInstance();
    };
    
    template<class MotionType>
    inline MotionType& Singleton<MotionType>::GetInstance()
    {
    	static MotionType Instance;
    	return Instance;
    }
    

    同时,遵循不在头文件以及类中书写函数实现的原则,可在GotoCave.cpp中实现GotoCave类中的构造函数。读者可根据程序需要灵活重载构造函数

    //GotoCave.h
    class GotoCave
    {
    private:
    	friend class Singleton<GotoCave>;
    	GotoCave();
            ~GotoCave();
    	GotoCave(const GotoCave&) = delete;
    	GotoCave& operator=(const GotoCave&) = delete;
    public:
    	//Whatever you want..
    };
    
    //GotoCave.cpp
    GotoCave::GotoCave() { 	//Codes
    }		
    GotoCave::~GotoCave() {	//Codes
    }
    

    饿汉式,当场直接实例化

    实现方法与懒汉式类似

    template<class MotionType>
    class Singleton
    {
    private:
    	Singleton();
    	Singleton(const Singleton&) = delete;
    	Singleton& operator=(const Singleton&) = delete;
            static MotionType Instance;
    public:
    	static MotionType& GetInstance();
    };
    
    template<class MotionType>
    MotionType Singleton<MotionType>::Instance;
    
    template<class MotionType>
    inline MotionType& Singleton<MotionType>::GetInstance()
    {
    	return Instance;
    }
    

    改进的懒汉式单例模板(未完善)

    使用智能指针接口,将类包装为线程安全的全局单例类

    其中涉及到的知识点有智能指针,单例模板,右值引用,锁以及变参模板

    变参模板使之能接受各种单例构造函数,并创建出实例

    //SmartSingleton.hpp
    #include<memory>
    #include<mutex>
    template<typename SType>
    class SmartSingleton
    {
    public:
    	template<typename ...Args>
    	static std::shared_ptr<SType> GetInstance(Args&&... args)
    	{
    		if (!pInstance)
    		{
    			std::lock_guard<std::mutex> lg(Mutex);
    			if (pInstance == nullptr)
                                    //pInstance = std::make_shared<SType>(std::forward<Args>(args)...);            //make缺点,调用不到私有的构造函数
    				pInstance = std::shared_ptr<SType>(new SType(std::forward<Args>(args)...));
    		}
    		return pInstance;
    	}
    private:
    	explicit SmartSingleton();
    	SmartSingleton(const SmartSingleton&) = delete;
    	SmartSingleton& operator=(const SmartSingleton&) = delete;
    private:
    	static std::shared_ptr<SType> pInstance;
    	static std::mutex Mutex;
    };
    template<typename SType>
    std::shared_ptr<SType> SmartSingleton<SType>::pInstance = nullptr;
    template<typename SType>
    std::mutex SmartSingleton<SType>::Mutex;
    

    测试类

    class MyClass
    {
            friend class SmartSingleton<MyClass>;
    private:
    	MyClass(std::string _name, int _num) : name(_name), num(_num) {}
    	std::string name;
    	int num;
    public:
    	~MyClass() { std::cout << "Destory" << std::endl; }
            //如果要写析构函数,则应将其设置为public
    };
    

    测试代码

    int main()
    {
    	auto p = SmartSingleton<MyClass>::GetInstance("Chen", 10);
            //Whatever you want with p..
    }
    
  • 相关阅读:
    使用java调用fastDFS客户端进行静态资源文件上传
    FastDFS概念、原理及CentOS7下安装实战
    Centos7安装Nginx实战
    Maven install [WARNING] The artifact aspectj:aspectjrt:jar:1.5.4 has been relocated to org.aspectj:aspectjrt:jar:1.5.4
    IOS照片颠倒分析及PHP服务端的处理
    web开发实战--弹出式富文本编辑器的实现思路和踩过的坑
    小木的智慧屋--微信公众号的推广案例分析(1)
    web开发实战--图片裁剪和上传
    springmvc学习笔记--ueditor和springmvc的集成
    网页闯关游戏(riddle webgame)--H5刮刮卡的原理和实践
  • 原文地址:https://www.cnblogs.com/tuapu/p/13826321.html
Copyright © 2020-2023  润新知