一、单例模式
单例模式确保一个类只有一个实例,自行提供这个实例并向整个系统提供这个实例。
特点就是多读单写。
函数简单实现:
class
Singleton
{
public
:
static
Singleton
*GetInstance()
//申明为static,可以由对象直接调用,不用用类调用
{
if
(_sInstance ==
NULL
)
{
_sInstance =
new
Singleton
;
}
return
_sInstance;
}
void
printf()
{
cout << _data << endl;
}
protected
:
Singleton()
:_data(0)
{}
//防拷贝,只申明不定义
Singleton(
Singleton
&);
Singleton
&operator=(
Singleton
&);
//指向实例的指针定义为静态私有,这样定义静态成员函数获取对象实例
static
Singleton
* _sInstance;
private
:
int
_data;
};
Singleton
*
Singleton
::_sInstance =
NULL
;
//初始化
int
main()
{
Singleton
::GetInstance()->printf();
return
0;
}
二、基于线程安全的改进
若有两个线程同时访问单例模式获取对象的方法,thread1执行方法,第一次进入发现指针为NULL,在还没有new之前,thread2也进入,这样就不能保证只有一个对象被创建,且第二个创建的对象将第一个创建的对象覆盖
解决方法:
1、懒汉模式
调用对象时才产生实例对象
改进
static
Singleton
*GetInstance()
{
if
(_sInstance ==
NULL
)
//保证效率
{
_mx.lock();
//保证线程安全
if
(_sInstance ==
NULL
)
{
_sInstance =
new
Singleton
;
//解法:RAII 2.用库函数的guard
}
_mx.unlock();
}
return
_sInstance;
}
新增static成员对象
static
mutex
_mx;
在类外需要初始化
mutex
Singleton
::_mx
但是,这么做有会引入新的问题,即当发生new失败时,则会发生抛异常, 而抛异常则会导致死锁。
解决方法:可以使用RAII,自动析构,guard
static
Singleton
*GetInstance()
{
if
(_sInstance ==
NULL
)
//保证效率
{
lock_guard
<
mutex
> lock(_mx);
if
(_sInstance ==
NULL
)
{
_sInstance =
new
Singleton
;
//new 有可能会抛异常,若抛异常则导致死锁,但是用lock_guard可以出作用域自动析构
}
}
return
_sInstance;
}
2、饿汉模式
最开始就创建对象
既线程安全,又高效
指针为静态,在main函数之前初始化,
class
Singleton
{
public
:
static
Singleton
*GetInstance()
{
assert
(_sInstance);
return
_sInstance;
}
void
printf()
{
cout << _data << endl;
}
protected
:
Singleton()
:_data(0)
{}
//防拷贝,只申明不定义
Singleton(
Singleton
&);
Singleton
&operator=(
Singleton
&);
//指向实例的指针定义为静态私有,这样定义静态成员函数获取对象实例
static
Singleton
* _sInstance;
private
:
int
_data;
};
Singleton
*
Singleton
::_sInstance =
new
Singleton
;
三、关于面试题
1、定义一个类,只能在堆上生成对象
class
A
{
public
:
A
*GetObj()
{
return
new
A
;
}
private
:
A()
{}
A(
const
A
&);
};
2、定义一个类,只能在栈上生成对象
class
A
{
private
:
void
*operator new (
size_t
);
void
operator delete(
void
*p);
void
*operator new[](
size_t
);
void
operator delete[](
void
*p);
private
:
int
a;
};