版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/jinsenianhua2012/article/details/82634179
C++ 单例模式设计
c++中,类对象被创建时,编译系统为对象分配内存空间,并自动调用构造函数,由构造函数完成成员的初始化工作,也就是说使用构造函数来初始化对象。
那么我们需要把构造函数设置为私有的 private,这样可以禁止别人使用构造函数创建其他的实例。
又单例类要一直向系统提供这个实例,那么,需要声明它为静态的实例成员,在需要的时候,才创建该实例。
且应该把这个静态成员设置为 null,在一个public 的方法里去判断,只有在静态实例成员为 null,也就是没有被初始化的时候,才去初始化它,且只被初始化一次。
- 但是如果使用全局或者静态变量方式,会影响其封装性,很难保证代码不会对全局变量做出修改或影响,一个好的办法是:让类自身负责保存其唯一实例
// 下面是一种单例示例
class Singleton {
public :
static Singleton* getInstance()
{
if(_instance == NULL)
{
_instance = new Singleton();
}
return _instance;
}
private :
// 设置为私有构造函数,防止外部调用实例化
Singleton()
{
cout <<"Instance"<<endl;
}
static Singleton* _instance;
};
Singleton * Singleton::_instance = NULL;
- 上述单例在单线程情况下没有问题,但是在多线程中就会出问题。比如两个线程同时请求并创建实例,便出问题了
为单例添加线程同步功能
// 考虑多线程下的单例情况, 互斥信号量解决方案
class Singleton{
public:
// get 方法
static Singleton* getSingleton()
{
// 通过互斥信号机制,为代码枷锁
lock();
// 判断是否为 NULL
if(NULL == instance)
{
instance = new Singleton();
}
// 使用完毕解锁
unlock();
return instance;
}
private:
Singleton(){
}
static Singleton* instance;
};
Singleton* Singleton::instance = NULL:
- 上面的方案缺点:每次get调用时都会同步加锁,如果线程比较多时,就会有大量的线程阻塞,且加锁也是个耗时的操作,所以除非有必要尽量避免加锁操作
// 采用双重判断的线程安全方式实现单例
class Singleton{
public :
// get方法
static Singleton* getInstance(){
// 先判断是否NULL,只有NULL时才需要加锁
if(NULL == instance)
{
lock();
// 再次判断是否NUKK (注意,这里需要再次判断)
if(NULL == instance)
{
instance = new Singleton();
}
unlock(); // 解锁
}
return instance;
}
private:
static Singleton* instance;
Singleton()
{
// 私有构造函数
}
};
Singleton* Singleton::instance = NULL;
- 饿汉式单例实现方式,在静态区先初始化 instance
class Singleton{
public:
//get 方法
static Singleton* getInstance()
{
return instance;
}
private:
Singleton(){}
static Singleton* instance;
};
// 直接初始化
Singleton* Singleton::instance = new Singleton();
注意:静态初始化实例可以保证线程安全,因为静态实例初始化在程序开始时进入主函数之前,就由主线程以单线程方式完成了初始化!饿汉式的单例类,也就是静态初始化实例保证其线程安全性,故在性能需求较高时,应使用这种模式,避免频繁的锁争夺。
- 注意!! 上面的单例模式没有 destory() 方法,也就是说,貌似上面的单例类没有主动析构这个唯一实例!然而这就导致了一个问题,在程序结束之后,该单例对象没有delete,导致内存泄露!下面是一些大神的方法:一个妥善的方法是让这个类自己知道在合适的时候把自己删除,或者说把删除自己的操作挂在操作系统中的某个合适的点上,使其在恰当的时候被自动执行。
- 我们知道,程序在结束的时候,系统会自动析构所有的全局变量。事实上,系统也会析构所有的类的静态成员变量。利用这些特征,我们可以在单例类中定义一个这样的静态成员变量,而它的唯一工作就是在析构函数中删除单例类的实例。如下面的代码中的Garbage类:
class Singleton{
public :
// get 方法
static Singleton* getInstance()
{
if(NULL == instance)
{
instance = new Singleton();
}
return instance;
}
//c++ 嵌套的内部类,作用是删除单例类对象,Garbage被定义为Singleton的内嵌类,以防该类被在其他地方滥用。
class Garbage{
public :
~Garbage(){
if(Singleton::instance != NULL)
{
delete Singleton::instance;
}
}
};
private :
//单例类中声明一个触发垃圾回收类的静态成员变量,它的唯一工作就是在析构函数中删除单例类的实例,利用程序在结束时析构全局变量的特性,选择最终的释放时机;
static Garbage garbage;
// 声明静态实例
static Singleton* instance;
Singleton(){}
};
//初始化内部的静态变量,目睹是启动删除的析构函数,如果不初始化,就不会被析构
//内部类可以访问外部类的私有成员,外部类不能访问内部类的私有成员!
Singleton::Garbage Singleton::garbage;
Singleton* Singleton::instance = NULL;
总结单例模式的特征:
只有一个类实例:
将构造函数私有化提供一个全局访问点:
类中创建静态成员和静态成员方法禁止拷贝:
把拷贝函数声明私有化,并且不提供实现;将赋值运算符私有化,防止对象被赋值
// 比较完整的 C++ 单例设计
class Singleton{
public :
// get 方法
static Singleton* getSingleton()
{
if(NULL == instance)
{
lock();
if(NULL == instance)
{
instance = new Singleton();
}
unlock();
}
return instance;
}
// c++ 嵌套的内部类,实现单例的删除操作
class Garbage{
public :
~Garbage()
{
if(Singleton::instance != NULL)
{
delete Singleton::instance;
}
}
};
private :
static Garbage garbage;//定义静态成员变量,利用程序在结束时会起购全局变量的特向释放单例
static Singleton *instance;
Singleton(){...}
~Singleton(){...}
Singleton(const Singleton ©);//私有化拷贝函数
Singleton & operate=(const Singleton &other); // 私有化赋值运算符
};
Singleton::Garbage Singleton::garbage;
Singleton* Singleton::instance = NULL;