c++ 单例的几种写法 singleton
单例的存在形式可以是,static对象,static指针,static智能指针。
static 对象相当于直接把对象生成在了栈中,不需要考虑生成时的多线程安全问题。
static指针在生成时需要注意只初始化一次,可以用call_once 或mutx实现线程安全。是用在多线程同时要访问改对象的场景。
static智能指针,智能指针只支持转移构造函数,不支持复制构造函数,所以全局改对象的指针只会存在在一个人的手里,而且自动析构。
由于单例往往是要写成模版类共其他类继承,所以需要定义单例模版类。
由于模版类无法自己编译,不能单独编译,所以只能存在头文件。
但是头文件也可以在头文件中include其他文件,从而在实现在看起来好像声明与定义分离了一样。
1 智能指针,call_once, 单文件模版类
Singleton.h
#ifndef SINGLETON_H
#define SINGLETON_H
#include <memory>
#include <mutex>
template<class T>
class Singleton
{
public:
static T* getInstancePtr();
protected:
// 这样搞就禁止了在外部创建对象,
Singleton() {};
~Singleton() {};
private:
// c98
// 在private中声明复制构造函数,但是不定义,这样就禁止了这个类被拷贝。
Singleton(const Singleton&);
Singleton& operator=(const Singleton&);
// c11
// 可以用delete声明这个函数禁用
// CopyBan(const CopyBan&) = delete;
// CopyBan& operator=(const CopyBan&) = delete;
private:
// 使用智能指针自动析构
static std::unique_ptr<T> _instance;
};
template<class T>
T* Singleton<T>::getInstancePtr()
{
static std::once_flag flag;
std::call_once(flag, [&]() {
_instance.reset(new T());
});
return _instance.get();
}
template<typename T>
std::unique_ptr<T> Singleton<T>::_instance = NULL;
#endif // !SINGLETON_H
简单说明:
public 里给出获取单例对象的接口 static T* getInstancePtr()
protected 中定义了构造函数和析构函数,使得无法在类外部访问构造函数,也就禁止了在类外生成对象。
private 声明了复制构造函数和转移构造函数,使得无法通过这两种方式生成对象。
使用智能指针自动管理对象的析构。同时由于智能指针只允许转移构造,不允许复制构造,可以保证同一时间只会有一个进程拿着这个指针。
在类外定义声明的静态函数时,不应该再加static了,并且要用模版类T给实现类,即getInstancePtr前面那个类得加\<T>
最后使用空指针初始化了智能指针。
2 将声明与定义分离
Singleton.h
#ifndef SINGLETON_H
#define SINGLETON_H
#include <memory>
#include <mutex>
template<class T>
class Singleton
{
public:
static T* getInstancePtr();
protected:
// 这样搞就禁止了在外部创建对象,
Singleton() {};
~Singleton() {};
private:
// c98
// 在private中声明复制构造函数,但是不定义,这样就禁止了这个类被拷贝。
Singleton(const Singleton&);
Singleton& operator=(const Singleton&);
// c11
// 可以用delete声明这个函数禁用
// CopyBan(const CopyBan&) = delete;
// CopyBan& operator=(const CopyBan&) = delete;
private:
// 使用智能指针自动析构
static std::unique_ptr<T> _instance;
};
#include "Singleton.tpp"
template<typename T>
std::unique_ptr<T> Singleton<T>::_instance = NULL;
#endif // !SINGLETON_H
Singleton.tpp
template<class T>
T* Singleton<T>::getInstancePtr()
{
static std::once_flag flag;
std::call_once(flag, [&]() {
_instance.reset(new T());
});
return _instance.get();
}
简答说明:
直接在头文件中include了tcpp文件,其实就是直接把tpp的内容放入了头文件中,但是在要定义的函数比较多的时候,看起来会清晰一点,
模版类由于不能单独编译,所以不存在cpp文件。
其实也不是这样说,你如果能够确定所有会使用这个模版类的类别,那么你可以直接在最后声明一下要实现的类,然后单独编译,只不过只能支持简单的类别,比如int,string,等等,如果你自己写的要继承这个模版类的类,那么你在这边就要引用那边类的头文件,那边又要引用这边的头文件,就形成交叉引用了,所以可以直接认为模版类不能单独编译是一点问题都没有的。
3 直接单例对象
class Singleton {
private: Singleton() { }
Singleton(const Singleton &) = delete;
Singleton(const Singleton &&) = delete;
Singleton &operator=(const Singleton &) = delete;
public: static Singleton &getInstance() {
static Singleton s;
return s;
}
public: void test() {
std::cout << "test" << std::endl;
}
};