一、单例模式
顾名思义就是一个项目中的某个类只有一个对象,不允许在外面new 出第二个对象
#if 1 //单例模式 :class MyClass
{
private:MyClass(){}static MyClass* m_instance; // public:static MyClass* getInstance(){if (m_instance == NULL){m_instance = new MyClass();}return m_instance;}void func(){cout << "MyClass :: func! \n";}
};//静态 在类外必须初始化
MyClass* MyClass::m_instance = NULL;
int main()
{MyClass* p = MyClass::getInstance();p->func();MyClass* p2 = MyClass::getInstance();p2->func();cout << "Hello World! \n";
}
#endif
从上面可以看出我们 p p2 都是同一个地址。,但是有个问题,这个单例对象如何来delete呢?
我们可以在这个单例模式中添加一个inner class ,用这个类来delete 我们的单例模式对象。
// 这个类就是用来回收上面MyClass 的这个单例对象class CGhuishou {public :~CGhuishou(){if (MyClass::m_instance){delete MyClass::m_instance;MyClass::m_instance = NULL;}}};
完整代码:
#if 1 //单例模式 : 懒汉式class MyClass
{
private:MyClass(){}static MyClass* m_instance; // public:static MyClass* getInstance(){if (m_instance == NULL){m_instance = new MyClass();static CGhuishou cg;}return m_instance;}// 这个类就是用来回收上面MyClass 的这个单例对象class CGhuishou {public :~CGhuishou(){if (MyClass::m_instance){delete MyClass::m_instance;MyClass::m_instance = NULL;}}};void func(){cout << "MyClass :: func! \n";}
};//静态 在类外必须初始化
MyClass* MyClass::m_instance = NULL;
int main()
{MyClass* p = MyClass::getInstance();p->func();MyClass* p2 = MyClass::getInstance();p2->func();cout << "Hello World! \n";
}
#endif
二、单例模式的线程安全问题分析
上述代码,如有多个线程来同事new 对象,线程是不是安全呢??
void mythread() {cout << "mythread ! \n";MyClass* p = MyClass::getInstance();cout << "mythread is over ! \n"; }// 两个线程 int main() {// 两个线程的入口函数都是 mythread ,thread t1(mythread);thread t2(mythread);t1.join();t2.join();cout << "Hello World! \n"; }
这种情况就会导致线程不安全
我们可以加锁:
mutex mymutex;
class MyClass
{
private:MyClass(){}static MyClass* m_instance; // public:static MyClass* getInstance(){// 为了解决这种模式的线程安全问题,我们可在这里加锁unique_lock<mutex> mylock(mymutex);if (m_instance == NULL){m_instance = new MyClass();static CGhuishou cg;}return m_instance;}// 这个类就是用来回收上面MyClass 的这个单例对象class CGhuishou{public:~CGhuishou(){if (MyClass::m_instance){delete MyClass::m_instance;MyClass::m_instance = NULL;}}};
但是这种方式执行效率太慢,在一般大数据中是不允许的,我么可以再次改进一下:
双重锁定:
static MyClass* getInstance()
{
if (m_instance == NULL)
{
// 为了解决这种模式的线程安全问题,我们可在这里加锁
unique_lock<mutex> mylock(mymutex);
if (m_instance == NULL)
{
m_instance = new MyClass();
static CGhuishou cg;
}
}
return m_instanc}
推荐的懒汉单例模式- 局部静态变量,是线程安全的:
#if 1 //推荐的懒汉式 模式 是线程安全的
class Singleton
{
public:~Singleton() {std::cout << "destructor called!" << std::endl;}Singleton(const Singleton&) = delete;Singleton& operator=(const Singleton&) = delete;static Singleton& get_instance(){static Singleton instance;return instance;}
private:Singleton() {std::cout << "constructor called!" << std::endl;}
};int main(int argc, char* argv[])
{Singleton& instance_1 = Singleton::get_instance();Singleton& instance_2 = Singleton::get_instance();return 0;
}
#endif
注意在使用的时候需要声明单例的引用 Single&
才能获取对象
另外网上有人的实现返回指针而不是返回引用
static Singleton* get_instance(){static Singleton instance;return &instance;
}
这样做并不好,理由主要是无法避免用户使用delete instance
导致对象被提前销毁。还是建议大家使用返回引用的方式。
C++ 单例模式总结与剖析 - 行者孙 - 博客园
三、call_once与单例模式
call_once的功能: 能保证函数a 只能调用一次,是具备互斥量能力的,而且更加高效。
使用:结合once_flag使用
static MyClass* getInstance()
{
call_once(gf,createInstance);// 如果多个线程都执行到这里的时候,第一个线程就会锁定,其他线程返回,gf 相当于是一般锁,这样也保证了线程的安全
return m_instance;
}
#if 1 //call_once 的使用:
once_flag gf; // 定义一个once_flag 的标记
class MyClass
{static void createInstance() // 这里是静态的 需注意{m_instance = new MyClass();}
private:MyClass(){}static MyClass* m_instance; // public:static MyClass* getInstance(){call_once(gf, createInstance);// 如果多个线程都执行到这里的时候,第一个线程就会锁定,其他线程返回,gf 相当于是一般锁,这样也保证了线程的安全return m_instance;}void func(){cout << "MyClass :: func! \n";}
};void mythread()
{cout << "mythread ! \n";MyClass* p = MyClass::getInstance();cout << "mythread is over ! \n";
}//静态 在类外必须初始化
MyClass* MyClass::m_instance = NULL;
int main()
{// 两个线程的入口函数都是 mythread ,thread t1(mythread);thread t2(mythread);t1.join();t2.join();cout << "Hello World! \n";
}
#endif