https://blog.csdn.net/king457757706/article/details/51887611
最近写代码因为疏忽单例的判断,导致程序快速切换多次启动时出现了bug ,借这个机会梳理一下几种单例模式的构造方式。单例模式存在的意义是保证一个类仅有一个实例,并提供一个访问它的全局访问点,该实例被所有程序模块共享。
- 解决方法1(懒汉式)
一种实现方法是定义一个单例类,使用类的私有静态指针变量指向类的唯一实例,并用一个公有的静态方法获取该实例。单例模式通过类本身来管理其唯一实例,这种特性提供了解决问题的方法。唯一的实例是类的一个普通对象,但设计这个类时,让它只能创建一个实例并提供对此实例的全局访问。唯一实例类Singleton在静态成员函数中隐藏创建实例的操作。
class CSingleton
{
private:
CSingleton() //构造函数是私有的
{
}
static CSingleton *m_pInstance;
public:
static CSingleton * GetInstance()
{
if(m_pInstance == NULL) //判断是否第一次调用
m_pInstance = new CSingleton();
return m_pInstance;
}
};
用户访问唯一实例的方法只有GetInstance()成员函数。如果不通过这个函数,任何创建实例的尝试都将失败,因为类的构造函数是私有的。GetInstance()使用懒惰初始化,也就是说它的返回值是当这个函数首次被访问时被创建的。所有GetInstance()之后的调用都返回相同实例的指针。
上面定义的CSingleton单例类有以下三个特征:(1 具有一个指向唯一实例的私有静态指针m_pInstance; (2 有一个可以获取这个唯一实例的公有函数,并且在需要的时候创建该实例; (3 构造函数私有,外界不可创建该类实例;
这种实现方式存在的一个问题是m_pInstance变量的释放问题,即实例的析构问题,虽然我们可以在程序结束时主动调用GetInstance()方法并对其返回的指针智行delete操作,但是这种方式不仅繁琐,而且调用者忘记的话,会引起很多的问题。
对于上面这种问题的一种解决方法是在该类中定义一个静态全局变量,我们知道,程序在结束的时候,系统会自动析构所有的全局变量。系统也会析构所有的类的静态成员变量,就像这些静态成员也是全局变量一样。我们可以在CSingleton单例类中定义一个这样的静态成员变量,在它的析构函数中删除单例类的实例。
class CSingleton
{
private:
CSingleton()
{
}
static CSingleton *m_pInstance;
class CGarbo //它的唯一工作就是在析构函数中删除CSingleton的实例
{
public:
~CGarbo()
{
if(CSingleton::m_pInstance)
delete CSingleton::m_pInstance;
}
};
static CGarbo Garbo; //定义一个静态成员变量,程序结束时,系统会自动调用它的析构函数
public:
static CSingleton * GetInstance()
{
if(m_pInstance == NULL) //判断是否第一次调用
m_pInstance = new CSingleton();
return m_pInstance;
}
};
- 解决方法2(饿汉式)
如果你对上面添加在单例类内部添加一个类静态对象的方法不是很满意,还可以使用局部静态变量方法构造:
class CSingleton
{
private:
CSingleton() //构造函数是私有的
{
}
public:
static CSingleton & GetInstance()
{
static CSingleton instance; //局部静态变量
return instance;
}
};
用此种方法会出现类拷贝的问题,例如 Singleton singleton = Singleton :: GetInstance();这种调用方式编译器会为类生成一个默认的构造函数,来支持类的拷贝。这样的话就违背了单例的特性,所以我们可以稍微改一下,返回的是指针:
class CSingleton
{
private:
CSingleton() //构造函数是私有的
{
}
public:
static CSingleton * GetInstance()
{
static CSingleton instance; //局部静态变量
return &instance;
}
};
也可以不让编译器这么做,显示的声明类拷贝的构造函数,重载 = 操作符。
原理参见《Effective C++》,第6章,不自动生成的函数,就明确拒绝。
为驳回编译器自动提供的功能,可将相应的成员函数声明为private并且不以实现。
class CSingleton
{
private:
CSingleton() //构造函数是私有的
{
}
CSingleton(const CSingleton &);
CSingleton & operator = (const CSingleton &);
public:
static CSingleton & GetInstance()
{
static CSingleton instance; //局部静态变量
return instance;
}
};