一、单例模式
单例模式是常见的一种软件设计模式,单例对象的类只能实例化一个对象。
该类负责创建对象,同时保证只能创建一个对象。并提供一个访问它的全局访问点,该实例被所有程序模块共享。
一般应用与工具类的实现或者消耗资源的场景。
特点:
- 类构造函数私有
- 持有自己类的引用
- 对外获取实例的静态方法
代码:
#include <iostream> #include <mutex> #include <thread> using namespace std; class singleton { public: static singleton* Getinstance() { //双重检查锁。 //加锁之前检查一次是否为空,加锁之后再检查一次。 if (nullptr == m_instance) { m_mtx.lock(); if (nullptr == m_instance) { m_instance = new singleton(); } m_mtx.unlock(); } return m_instance; } //实现一个内嵌垃圾回收类 class CGarbo{ public: ~CGarbo(){ if (singleton::m_instance) delete singleton::m_instance; } }; //定义一个静态成员变量,程序结束时,会自动调用它的析构函数从而释放单例对象。 static CGarbo Garbo; private: //构造函数私有 singleton(){}; //防拷贝 singleton(singleton const&); singleton& operator=(singleton const&); static singleton* m_instance;//单例对象指针 static mutex m_mtx;//互斥锁 }; singleton* singleton::m_instance = nullptr; singleton::CGarbo Garbo; mutex singleton::m_mtx; void func(int n) { cout << singleton::Getinstance() << endl; } int mian() { thread t1(func, 10); thread t2(func, 10); t1.join(); t2.join(); cout << singleton::Getinstance() << endl; cout << singleton::Getinstance() << endl; }
- 需要频繁实例化然后销毁的对象。
- 创建对象时耗时过多或者耗资源过多,但又经常用到的对象。
- 有状态的工具类对象。
- 频繁访问数据库或文件的对象
二、C++ 类的默认八种函数
在C++中,一个类有八个默认函数:
1、默认构造函数; 2、默认拷贝构造函数; 3、默认析构函数; 4、默认重载赋值运算符函数; 5、默认重载取址运算符函数; 6、默认重载取址运算符const函数; 7、默认移动构造函数(C++11); 8、默认重载移动赋值操作符函数(C++11)。
在 C++ 中,如果我们定义了一个空类
编译器会自动为这个类生成构造函数、析构函数等。具体来说,我们定义了以上的一个 Dog
类,编译器会将它补全成一个如下自带 6 个函数的类:
class Dog { // C++03 Dog(); // 默认构造函数 Dog(const Dog&); // 拷贝构造函数 ~Dog() // 析构函数 // 拷贝赋值运算符 Dog& operator=(const Dog&); // C++11 新增 Dog(Dog&&); // 移动构造函数 // 移动赋值运算符 Dog& operator=(Dog&&); };
一般的书上好像都是前面四种:默认构造函数,拷贝构造函数,默认赋值函数以及析构函数,
Dog*operator&(); // 取址运算符 const Dog*operator&() const; // 取址运算符const
这两种其实属于重载运算符,但要需要注意的是,只有当你需要用到这些函数的时候,编译器才会去定义它们。
三、论单例模式内存释放
(一)绝对不要在析构函数中释放单例
我想说, 单例伴随着进程的生命周期, 常驻内存, 不需要程序员来释放(实际上, 人为释放是有风险的)。 如果进程终结, 对应的堆内存自动被回收, 不会泄露。
#include <iostream> using namespace std; class A { private: static A *m_p; public: static A *getSingleTon() { if (NULL == m_p) { m_p = new A(); } return m_p; } ~A() { if (NULL != m_p) { delete m_p; m_p = NULL; } } }; A *A::m_p = NULL; int main() { return 0; }
大家可以看看上述程序有什么问题。
如果没有看出问题, 那你再运行一下如下程序:
#include <iostream> using namespace std; class A { private: static A* m_p; public: static A* getSingleTon() { if (NULL == m_p) { m_p = new A(); } return m_p; } ~A() { if (NULL != m_p) { cout << "xxx" << endl; delete m_p; // 递归调用析构 m_p = NULL; cout << "yyy" << endl; // 永远也不会执行 } } }; A* A::m_p = NULL; int main() { A* p = A::getSingleTon(); delete p; return 0; }
输出:
xxx
xxx
........
运行发现, 析构函数被多次调用了, 为什么呢?当类的使用者调用delete p;的时候, 实际上就是调用析构函数来释放单例, 但是, 现在类的提供者在析构函数中又delete这个单例, 显然又会调用析构函数, 所以形成了递归调用析构函数, 系统不异常才怪呢。
我们来反思一下, 为什么会出上述问题呢?肯定是写SingleTon的人牢牢记住了: 要在析构函数中释放资源。 但是, 他不明白, 单例应该由类的使用者来释放, 而不是类的提供者。 不要把角色搞错了。
千万不要说, 为什么出这么低级的问题! 其实, 这个问题不低级, 是个比较隐蔽的错误。 而且, 当代码多了(比如100w行), 离职的人多了, 经几次交接后, 那代码就可想而知了哭
下面, 我们继续看看:
#include <iostream> using namespace std; class A { private: static A* m_p; int x; A() { x = 1; } public: static A* getSingleTon() { if (NULL == m_p) { m_p = new A(); } return m_p; } ~A() { if (NULL != m_p) { cout << "xxx:" << x << endl; // 永远是xxx1 delete m_p; // 递归调用析构 m_p = NULL; cout << "yyy:" << x << endl; // 永远也不会执行, 单例也不会被释放 } } }; A* A::m_p = NULL; int main() { A* p = A::getSingleTon(); delete p; return 0; }
输出:
xxx:1
xxx:1......
从结果看, x的值一直是1, 所以单例根本就没有析构掉, 也就是说, 没有执行析构函数右边的花括号处, 单例就不会被释放。
实际上, 要快速定位到析构函数的问题, 还是很不容易的, 那么多代码, 进程死掉, 咋快速定位?尤其是, 如果析构函数中没有日志打印, 根本就很难知道析构函数被多次执行了。 所以, 关于日志, 我强烈建议:
1. 所有的构造函数和析构函数都必须有日志打印。
2. 不被频繁调用的函数中, 必须有日志(很多人只喜欢在某些异常分支打日志, 甚至连异常分支都不打印日志, 确实太流氓了)
(二)单例模式的自动释放
1.借助友元类
//利用友元类,实现单例模式的自动释放 #include <stdio.h> #include <iostream> using std::cout; using std::endl; using std::cin; class AutoRelease; class Singleton{ //单例模式的类 public: static Singleton *getInstance();//返回单例指针 private: friend class AutoRelease; Singleton(); //构造函数和析构函数都得是private ~Singleton(); static Singleton *_pInstance; }; Singleton *Singleton::getInstance(){ if(_pInstance == nullptr){ _pInstance = new Singleton(); } return _pInstance; } Singleton::Singleton() { cout << "Singleton()" << endl; } Singleton::~Singleton(){ cout << "~Singleton()" << endl; } class AutoRelease{ //用来实现单例的自动释放的类 //应该保存在栈上,程序结束时自动回收单例的资源 public: AutoRelease(){ cout << "AutoRelease()" << endl; } ~AutoRelease(){ cout << "~AutoRelease()" << endl; if(Singleton::_pInstance == nullptr){ return; } delete Singleton::_pInstance; Singleton::_pInstance = nullptr; } }; Singleton *Singleton::_pInstance = nullptr; //饱汉模式 int main() { Singleton *s1 = Singleton::getInstance(); Singleton *s2 = Singleton::getInstance(); AutoRelease at; printf("s1 = %p\n", s1); printf("s2 = %p\n", s2); s1 = nullptr; s2 = nullptr; return 0; }
2.借助内部类和静态数据成员
//利用内部类,实现单例模式的自动释放 #include <stdio.h> #include <iostream> using std::cout; using std::endl; using std::cin; class Singleton{ //单例模式的类 public: static Singleton *getInstance();//返回单例指针 private: friend class AutoRelease; Singleton(); //构造函数和析构函数都得是private ~Singleton(); static Singleton *_pInstance; private: //应该设计为私有类,避免类外的其他成员使用 class AutoRelease{ //用来实现单例的自动释放的内部类 //应该保存在栈上,程序结束时自动回收单例的资源 public: AutoRelease(){ cout << "AutoRelease()" << endl; } ~AutoRelease(){ cout << "~AutoRelease()" << endl; if(Singleton::_pInstance == nullptr){ return; } delete Singleton::_pInstance; Singleton::_pInstance = nullptr; } }; private: static AutoRelease _at; //由于AutoRelease是private,所以对象应该放在静态区 }; Singleton *Singleton::getInstance(){ if(_pInstance == nullptr){ _pInstance = new Singleton(); } return _pInstance; } Singleton::Singleton() { cout << "Singleton()" << endl; } Singleton::~Singleton(){ cout << "~Singleton()" << endl; } /* Singleton *Singleton::_pInstance = nullptr; //饱汉模式 */ //饱汉模式多线程时不安全,需要使用饿汉模式,在程序跑起来前就生成单例对象 Singleton *Singleton::_pInstance = Singleton::getInstance();//饿汉模式 Singleton::AutoRelease Singleton::_at; int main() { Singleton *s1 = Singleton::getInstance(); Singleton *s2 = Singleton::getInstance(); printf("s1 = %p\n", s1); printf("s2 = %p\n", s2); s1 = nullptr; s2 = nullptr; return 0; }
3.借助atexit()函数
//利用atexit函数,实现单例模式的自动释放 #include <stdio.h> #include <iostream> using std::cout; using std::endl; using std::cin; class Singleton{ //单例模式的类 public: static Singleton *getInstance();//返回单例指针 static void destroy(); private: friend class AutoRelease; Singleton(); //构造函数和析构函数都得是private ~Singleton(); static Singleton *_pInstance; }; Singleton *Singleton::getInstance(){ if(_pInstance == nullptr){ _pInstance = new Singleton(); //注册destroy函数,在进程结束的时候执行,从而自动回收单例 atexit(Singleton::destroy); } return _pInstance; } void Singleton::destroy(){ if(Singleton::_pInstance == nullptr){ return; } delete Singleton::_pInstance; Singleton::_pInstance = nullptr; } Singleton::Singleton() { cout << "Singleton()" << endl; } Singleton::~Singleton(){ cout << "~Singleton()" << endl; } //为了保证多线程情况下的安全性,使用饿汉模式 Singleton *Singleton::_pInstance = Singleton::getInstance(); //饿汉模式 int main() { Singleton *s1 = Singleton::getInstance(); Singleton *s2 = Singleton::getInstance(); printf("s1 = %p\n", s1); printf("s2 = %p\n", s2); s1 = nullptr; s2 = nullptr; return 0; }
4.借助pthread_once和atexit函数
//利用pthread_once和atexit函数,实现单例模式的自动释放 #include <stdio.h> #include <iostream> using std::cout; using std::endl; using std::cin; class Singleton{ //单例模式的类 public: static void init(); static Singleton *getInstance();//返回单例指针 static void destroy(); private: friend class AutoRelease; Singleton(); //构造函数和析构函数都得是private ~Singleton(); static pthread_once_t _once; static Singleton *_pInstance; }; void Singleton::init(){ //初始化单例,注册回收函数 if(_pInstance == nullptr){ _pInstance = new Singleton(); atexit(Singleton::destroy); } } Singleton *Singleton::getInstance(){ //执行pthread_once,保证在多线程的情况下创建单例对象的安全性 pthread_once(&_once, init); return _pInstance; } void Singleton::destroy(){ if(Singleton::_pInstance == nullptr){ return; } delete Singleton::_pInstance; Singleton::_pInstance = nullptr; } Singleton::Singleton() { cout << "Singleton()" << endl; } Singleton::~Singleton(){ cout << "~Singleton()" << endl; } //由于已经使用了pthread_once来保证安全性,所以使用饱汉模式即可 Singleton *Singleton::_pInstance = nullptr; /* Singleton *Singleton::_pInstance = Singleton::getInstance(); //饿汉模式 */ pthread_once_t Singleton::_once = PTHREAD_ONCE_INIT; int main() { Singleton *s1 = Singleton::getInstance(); Singleton *s2 = Singleton::getInstance(); printf("s1 = %p\n", s1); printf("s2 = %p\n", s2); s1 = nullptr; s2 = nullptr; return 0; }
标签:pInstance,Singleton,函数,代码,nullptr,模式,static,单例 From: https://www.cnblogs.com/imreW/p/17718182.html