下面先说说为什么它不安全:
class JackTang
{
private:
JackTang()
{
}
//不能在析构函数中析构这个静态对象,这是一种错误的做法,虽然会调用,但是一个死递归,正确做法
//是创建一个嵌套析构类(下面的B)。
/*~JackTang()
{
if (pInstance)
{
cout << "hello ";
delete pInstance;//递归调用
pInstance = NULL;
cout << "world!\n";//永远也不会执行
}
}*/
static JackTang *pInstance;
class B
{
public:
~B()
{
if (JackTang::pInstance)
{
delete JackTang::pInstance;
JackTang::pInstance = NULL;
}
}
};
public:
static JackTang * GetInstance()
{
if (!pInstance)
{
cout << "创建实例**************************************\n";
pInstance = new JackTang;
}
static B b;
return pInstance;
}
void Func()
{
cout << "测试\n";
}
};
JackTang * JackTang::pInstance = NULL;
void play()
{
JackTang *pINstance = JackTang::GetInstance();
pINstance->Func();
JackTang *pINstance1 = JackTang::GetInstance();
pINstance1->Func();
}
void main()
{
for (int i = 0; i < 100;i++)
{
std::thread t1(play);
t1.detach();
}
cout << "I love China!\n";
system("pause");
}
结果:
从结果中可以看出,实际创建了多个实例。
那该如何解决呢?
第一种方法:加锁
关键代码如下:
static JackTang * GetInstance()
{
m_utex1.lock();
if (!pInstance)
{
cout << "创建实例**************************************\n";
pInstance = new JackTang;
}
m_utex1.unlock();
static B b;
return pInstance;
}
但这个代码是非常低效的,如果要频繁调用,假设要调用10000次,那每次都得加锁,解锁,效率就会很低。一般大神是不允许这样的事情发生。所以我们需要做一个改进,如下:
static JackTang * GetInstance()
{
//这一行为了提高效率,也就是只有第一次的时候加锁。
if (NULL == pInstance)
{
m_utex1.lock();
if (NULL == pInstance)
{
cout << "创建实例**************************************\n";
pInstance = new JackTang;
}
m_utex1.unlock();
}
static B b;
return pInstance;
}
第二种解决办法:饿汉式
class JackTang
{
private:
JackTang()
{
}
//不能在析构函数中析构这个静态对象,这是一种错误的做法,虽然会调用,但是一个死递归,正确做法
//是创建一个嵌套析构类(下面的B)。
/*~JackTang()
{
if (pInstance)
{
cout << "hello ";
delete pInstance;//递归调用
pInstance = NULL;
cout << "world!\n";//永远也不会执行
}
}*/
static JackTang *pInstance;
class B
{
public:
~B()
{
if (JackTang::pInstance)
{
delete JackTang::pInstance;
JackTang::pInstance = NULL;
}
}
};
public:
static JackTang * GetInstance()
{
static B b;
return pInstance;
}
void Func()
{
cout << "测试\n";
}
};
//饿汉式
JackTang * JackTang::pInstance = new JackTang();
第三种解决办法:std::call_once()
c++11引入的函数,该寒素的第二个参数,一个函数名a(),功能是能够保证函数a()只被调用一次,call_once具备互斥量这种能力,而且效率上,比互斥量消耗资源更少。但call_once()需要与一个标记std::once_flag配合使用,其实once_flag是一个结构;call_once()就是通过这个标记来决定对应的 函数a()是否执行,调用call_once()成功后,call_once()就把这个标记设置为已调用的状态,后续嗲用call_once(),只要once_flag被设置为了“已调用”状态,那么对应的函数a()就不会被再调用了。
下面是完整代码:
class JackTang
{
private:
JackTang()
{
}
//不能在析构函数中析构这个静态对象,这是一种错误的做法,虽然会调用,但是一个死递归,正确做法
//是创建一个嵌套析构类(下面的B)。
/*~JackTang()
{
if (pInstance)
{
cout << "hello ";
delete pInstance;//递归调用
pInstance = NULL;
cout << "world!\n";//永远也不会执行
}
}*/
static JackTang *pInstance;
class B
{
public:
~B()
{
if (JackTang::pInstance)
{
delete JackTang::pInstance;
JackTang::pInstance = NULL;
}
}
};
public:
static void createInstance()
{
Sleep(20000);
cout << "创建实例*******************************\n";
pInstance = new JackTang;
static B b;
}
static JackTang * GetInstance()
{
std::call_once(flag, createInstance);
return pInstance;
}
void Func()
{
cout << "测试\n";
}
};
JackTang *JackTang::pInstance = NULL;
void play()
{
JackTang *pINstance = JackTang::GetInstance();
pINstance->Func();
JackTang *pINstance1 = JackTang::GetInstance();
pINstance1->Func();
}
void main()
{
for (int i = 0; i < 100;i++)
{
std::thread t1(play);
t1.detach();
}
cout << "I love China!\n";
system("pause");
}
结果:
但是我还是推介使用加锁的方式(改进版)。
如果觉得本文对你有用,可以使用微信扫一扫支持一下。