单例模式
首先什么是单例模式:
《设计模式》一书中给出的定义是:让类自身负责保存它的唯一实例,这个类可以保证没有其他实例被创建(通过截取创建新对象的请求),并且它可以提供一个访问该实例的方法,这就是Singleton模式。有人叫单例模式,也有叫单件模式。那么如何实现以上要求呢?首先,需要保证一个类只有一个实例;在类中,要构造一个实例,就必须调用类的构造函数,如此,为了防止在外部调用类的构造函数而构造实例,需要将构造函数的访问权限标记为protected或private;最后,需要提供要给全局访问点,就需要在类中定义一个static函数,返回在类内部唯一构造的实例。
####单例大约有两种实现方法:懒汉与饿汉。
懒汉:故名思义,不到万不得已就不会去实例化类,也就是说在第一次用到类实例的时候才会去实例化;
饿汉:饿了肯定要饥不择食。所以在单例类定义的时候就进行实例化。
###特点与选择:
由于要进行线程同步,所以在访问量比较大,或者可能访问的线程比较多时,采用饿汉实现,可以实现更好的性能。这是以空间换时间。
在访问量较小时,采用懒汉实现。这是以时间换空间。
###C++代码实现
- 经典版:
#include <iostream>
using namespace std;
//懒汉模式
class Singleton
{
public:
/**
*需要提供要给全局访问点,就需要在类中定义一个static函数,返回在类内部唯一构造的实例
*/
static Singleton *GetInstance()
{
if (m_Instance == NULL )
{ m_Instance = new Singleton ();
}
return m_Instance;
} static void DestoryInstance()
{
if (m_Instance != NULL )
{ delete m_Instance; m_Instance = NULL ;
}
}
private:
/**
*构造函数卸载私有里,为了防止在外部调用类的构造函数而构造实例
*/
Singleton();
static Singleton *m_Instance;
};
Singleton *Singleton ::m_Instance = NULL;
int main(int argc , char *argv [])
{
Singleton *singletonObj = Singleton ::GetInstance();
Singleton ::DestoryInstance();
return 0;
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
这是最简单,也是最普遍的实现方式,也是现在网上各个博客中记述的实现方式,但是,这种实现方式,有很多问题,比如:没有考虑到多线程的问题,在多线程的情况下,就可能创建多个Singleton实例,以下版本是改善的版本。
- 经典版优化:-
#include <iostream>
using namespace std;
//懒汉模式
class Singleton
{
public:
/*
此处进行了两次m_Instance == NULL的判断,是借鉴了Java的单例模式实现时,使用的所谓的“双检锁”机制。
因为进行一次加锁和解锁是需要付出对应的代价的,而进行两次判断,就可以避免多次加锁与解锁操作,同时也
保证了线程安全。但是,这种实现方法在平时的项目开发中用的很好,也没有什么问题?但是,如果进行大数据
的操作,加锁操作将成为一个性能的瓶颈;为此,一种新的单例模式的实现也就出现了。
*/
static Singleton *GetInstance()
{
if (m_Instance == NULL )
{ Lock(); if (m_Instance == NULL ) { m_Instance = new Singleton (); } UnLock(); }
return m_Instance;
} static void DestoryInstance()
{
if (m_Instance != NULL )
{ delete m_Instance; m_Instance = NULL ;
}
}
private:
Singleton();
static Singleton *m_Instance;
};
Singleton *Singleton ::m_Instance = NULL;
int main(int argc , char *argv [])
{
Singleton *singletonObj = Singleton ::GetInstance();
Singleton ::DestoryInstance();
return 0;
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 内部静态变量的懒汉实现:-
此方法也很容易实现,在instance函数里定义一个静态的实例,也可以保证拥有唯一实例,在返回时只需要返回其指针就可以了。推荐这种实现方法,真得非常简单。
#include <iostream>
using namespace std;
class Singleton
{
public:
static Singleton *GetInstance()
{
lock();
static Singleton m_Instance;
unlock();
return &m_Instance;
}
private:
Singleton();
};
int main(int argc , char *argv [])
{
Singleton *singletonObj = Singleton ::GetInstance();
cout<<singletonObj->GetTest()<<endl; singletonObj = Singleton ::GetInstance();
cout<<singletonObj->GetTest()<<endl;
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
因为static Singleton *GetInstance()是不可重入函数,所以存在线程安全问题,访问静态变量时要加锁;那么一个函数要成为可重入函数,必须要具备以下几个特点:
1、仅依赖于调用方传入的参数
2、不依赖于任何单个资源的锁
3、不反回任何(局部)静态或全局的非const变量的指针
4、不使用任何(局部)静态或全局的非const变量
5、不调用任何不可重入函数
可重入是并发安全的强力保证,一个可重入函数可以在多线程环境下放心使用。
- 饿汉实现:-
#include <iostream>
using namespace std;
class Singleton
{
public:
static Singleton *GetInstance()
{
return m_instace;
}
private:
Singleton();
static Singleton *m_instance;
};
Singleton* Singleton :: m_instance = new Singleton();
int main(int argc , char *argv [])
{
Singleton *singletonObj = Singleton ::GetInstance();
singletonObj = Singleton ::GetInstance();
return 0;
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
因为静态初始化在程序开始时,也就是进入主函数之前,由主线程以单线程方式完成了初始化,所以静态初始化实例保证了线程安全性。在性能要求比较高时,就可以使用这种方式,从而避免频繁的加锁和解锁造成的资源浪费。
- 实例销毁:-
#include <iostream>
using namespace std;
class Singleton
{
public:
static Singleton *GetInstance()
{
return m_Instance;
} int GetTest()
{
return m_Test;
}
private:
Singleton(){ m_Test = 10; }
static Singleton *m_Instance;
int m_Test; // This is important
class GC
{
public :
~GC()
{ // We can destory all the resouce here, eg:db connector, file handle and so on if (m_Instance != NULL ) { cout<< "Here is the test" <<endl; delete m_Instance; m_Instance = NULL ; }
}
};
static GC gc;
};
Singleton *Singleton ::m_Instance = new Singleton();
Singleton ::GC Singleton ::gc;
int main(int argc , char *argv [])
{
Singleton *singletonObj = Singleton ::GetInstance();
cout<<singletonObj->GetTest()<<endl; return 0;
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
文章来源: blog.csdn.net,作者:IM-STONE,版权归原作者所有,如需转载,请联系作者。
原文链接:blog.csdn.net/doubleintfloat/article/details/79824804