Loading

C++ 单例模板

懒汉式

懒汉式:比较懒,所以用的时候才初始化,所以是线程不安全的。所以需要做出一点小技巧让它变得线程安全

由于单例模板类本身不需要被创建拷贝或赋值,因此可以删除

template<typename MotionType>
class Singleton
{
public:
    Singleton() = delete;
    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
{
public:
    Singleton() = delete;
    Singleton(const Singleton&) = delete;
    Singleton& operator=(const Singleton&) = delete;
    static MotionType* GetInstance();
private:
    static MotionType* pInstance;
};

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

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

在构建好懒汉式单例模板类后,可以设计一个具体的类来测试模板类是否工作正常

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

class GotoCave
{
private:
    friend class Singleton<GotoCave>;
    GotoCave() {
        // Init your class
    }
    ~GotoCave() {
        // Freed your class
    }
public:
    GotoCave(const GotoCave&) = delete;
    GotoCave& operator=(const GotoCave&) = delete;

    // Whatever you want...
};

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

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

但是,以上代码存在问题。pInstance作为模板类的成员变量,在程序结束的时候并不会调用GotoCave的析构函数,这样就会导致内存泄漏;并且在多线程的情况下,某个线程执行new的同时,另一个线程可能正在对pInstance判空,而此时实例还未被创建出来,这并不是我们期望的结果。在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
{
public:
    Singleton();
    Singleton(const Singleton&) = delete;
    Singleton& operator=(const Singleton&) = delete;
    static MotionType& GetInstance();
};

template<class MotionType>
inline MotionType& Singleton<MotionType>::GetInstance()
{
    static MotionType Instance;
    return Instance;
}

饿汉式

饿汉式,比较饿,程序一开始就迫不及待的进行初始化,因为程序开始运行时就创建完毕,多线程只有读操作,所以是线程安全的

实现方法与懒汉式类似

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

template<class MotionType>
MotionType Singleton<MotionType>::Instance;

template<class MotionType>
inline MotionType& Singleton<MotionType>::GetInstance()
{
    return Instance;
}

由于使用了模板类,因此只有代码中使用到对应的单例模板时,编译期才会生成相应的单例类代码。由于是饿汉式单例,具体类的初始化还是还是会执行在main函数之前

更深层次的懒汉式单例

  • 使用智能指针管理内存,解决了析构的问题
  • 使用可变模板参数,解决了不同类的构造问题
  • 使用锁,解决了多线程下读写的安全性问题
// SmartSingleton.hpp
#include<memory>
#include<mutex>

template<typename SType>
class SmartSingleton
{
public:
    template<typename... Args>
    static std::shared_ptr<SType> GetInstance(Args&&... args)
    {
        std::lock_guard<std::mutex> lg(mutexLock);
        if (pInstance == nullptr)
        {
            // 缺点 调用不到私有的构造函数 除非对应类将std::make_shared设置为友元函数
            // pInstance = std::make_shared<SType>(std::forward<Args>(args)...);
            pInstance = std::shared_ptr<SType>(new SType(std::forward<Args>(args)...));
        }
        return pInstance;
    }

    SmartSingleton() = delete;
    SmartSingleton(const SmartSingleton&) = delete;
    SmartSingleton& operator=(const SmartSingleton&) = delete;
private:
    static std::shared_ptr<SType> pInstance;
    static std::mutex mutexLock;
};

template<typename SType>
std::shared_ptr<SType> SmartSingleton<SType>::pInstance = nullptr;

template<typename SType>
std::mutex SmartSingleton<SType>::mutexLock;

测试类

class MyClass
{
    friend class SmartSingleton<MyClass>;
private:
    MyClass(std::string _name, int _num) : name(std::move(_name)), num(_num) { std::cout << "MyClass" << std::endl; }
    std::string name;
    int num;
public:
    ~MyClass() { std::cout << "~MyClass" << std::endl; }
};

测试代码

int main()
{
    std::shared_ptr<MyClass> p = SmartSingleton<MyClass>::GetInstance("Chen", 10);
    // Whatever you want to do with p...
}
posted @ 2020-10-16 14:30  _FeiFei  阅读(534)  评论(1编辑  收藏  举报