1. 单例模式的定义:
单例模式属于创建者模式,顾名思义,即保证为一个类只生成唯一的实例对象。其结构可以简单粗暴地用以下类图来表示:
由其类图可见,单例的成员仅有类型为其自身Singleton的参数Instance,而方法为构造函数和单例实现函数GetInstance()。其中GetInstance为静态方法,主要负责创建自己的唯一实例(在创建完成之后,返回所创建的自己)
2. 单例模式的使用情况
单例模式主要就是用于当且仅当该对象只能创建出一个时,必须保证该对象是唯一时的情况,或者需要创建一个共同体来共享资源时。
3. 单例模式的分类以及代码实现
3.1 懒汉式
只有在获取单例时(调用GetInstance()函数时),才会new出单例的对象,其具体的代码如下:
class Singleton //单例模式懒汉式,当其被获取单例时(GetInstance时),才会对单例进行NEW操作
{
public:
Singleton() { cout << "懒汉构造函数执行" << endl; }
static Singleton* GetInstance()
{
if (m_sig == nullptr)
{
m_sig = new Singleton();
cout << "懒汉单例实现" << endl;
}
return m_sig;
}
void Free()
{
if (m_sig != nullptr)
{
delete m_sig;
m_sig = nullptr;
}
cout << "懒汉析构执行" << endl;
}
private:
static Singleton* m_sig;
};
Singleton* Singleton::m_sig = nullptr; //类外定义初始值
这里值得一提的是,懒汉式的内部对象m_sig是静态对象,其初始值nullptr的定义在类外进行。
3.2 饿汉式
饿汉式单例即在函数开始初始化的阶段,直接在全局变量区就new出了单例对象。即在开始执行main函数前就已经定义了单例。其具体代码如下:
class Singleton_Ehan //单例模式饿汉式,其内部静态变量在创建时即NEW出,不需要在被获取时创建
{
public:
Singleton_Ehan() { cout << "饿汉构造函数执行" << endl; }
static Singleton_Ehan* GetInstance()
{
return m_esig;
}
void Free()
{
if (m_esig != nullptr)
{
delete m_esig;
m_esig = nullptr;
}
cout << "饿汉析构执行" << endl;
}
private:
static Singleton_Ehan* m_esig;
};
Singleton_Ehan* Singleton_Ehan::m_esig = new Singleton_Ehan(); //类外直接赋值
这里指的一提的是,饿汉式的静态变量在初始化时就直接new出来了,而不是懒汉式那样还需要调用GetInstance函数。同时不同的还有n调用GetInstance函数时,懒汉式是先new再return,而饿汉式由于已经提前定义了就直接return了。
3.3 测试时使用的主函数
int main_singleton()
{
Singleton* temp1 = Singleton::GetInstance();
Singleton* temp2 = Singleton::GetInstance();
Singleton_Ehan* temp3 = Singleton_Ehan::GetInstance();
Singleton_Ehan* temp4 = Singleton_Ehan::GetInstance();
if (temp1 == temp2)
cout << "懒汉相同" << endl;
else
cout << "懒汉不相同" << endl;
if (temp3 == temp4)
cout << "饿汉相同" << endl;
else
cout << "饿汉不相同" << endl;
temp1->Free();
temp2->Free();
temp3->Free();
temp4->Free();
return 0;
}
4. 单例模式的一些问题
目前我所已知的问题,就是懒汉式在面对多线程情况下的“失效”问题:
由于懒汉式的new的过程发生在调用GetInstance函数的过程中,而函数的执行是需要一定的时间的,所以当多个线程同时执行懒汉式的单例GetInstance函数时,可能会导致new出多个类的情况发生,这是与单例模式的本例相违背的。
目前具体的解决方式即为线程同步的方式,其中最为常用的即为lock+unlock这样的互斥量连锁控制方式,可以保证new的这段代码有且只能让一个线程在使用,从而从根本上避免这种“乌龙”的情况产生。