单例模式是指一个类只能创建一个类对象,本文从第一种单例模式的写法开始介绍,后一种单例模式均是对前一种单例模式可能出现的问题的改进,写法大同小异,读者可跟随文章一起分析。
第一种单例模式#includeusing namespace std; class Clog { private: static Clog* ptr; Clog() = default; public: //唯一的创建类的接口 static Clog* createObject(){ if(ptr==nullptr) ptr=new Clog(); return ptr; } }; Clog* Clog::ptr = nullptr; int main() { Clog* p = Clog::createObject(); Clog* p1 = Clog::createObject(); delete p; delete p1; //指针接收创建这个唯一的类就是需要手动释放资源,容易引起线程安全问题 }
分析
1)将构造函数声明为私有,而暴露一个createObject()函数创造类对象。
2)将createObject()类对象声明为static,方便我们直接用类名调用该函数,而不必陷入“调用函数应该先创建对象,而创建对象需要调用该函数的这种鸡生蛋,蛋生鸡” 问题。
3)现在需要一个Clog类的指针作为createObject()函数的返回值,而只有静态成员变量才能不用对象就可以调用,而且静态函数内部只能用静态成员变量,所以在类中声明一个静态的Clog指针。
4)在main函数中直接调用Clog类中的createObject方法,会返回一个Clog类对象的指针=Clog::ptr,是Clog类中的静态成员变量,所以不论在main函数中调用多少次,返回的都是唯一的那一个Clog类中的静态成员变量ptr的值。
5)从此我们想要在程序的任何地方使用该单例对象都可以直接调用Clog类中的createObject方法返回这个对象供我们操作。也就是说实现了单例模式,单例模式有什么用呢?比如说我们编程序的时候想要记录我们的日志,那么就可以用这个唯一的单例模式的类对象进行记录。
该方法的不足之处
createObject()函数返回的是Clog*类型new出来的堆对象,我们需要注意内存释放的问题。容易引起线程安全性问题。
----->解决方法:createObject()函数返回Clog 类型对象栈对象,并用static关键字声明该对象,让该对象的声明周期在整个程序运行期间。(这里不理解可以看看static关键字用法)见第二种单例模式。
//#includeusing namespace std; class Clog { private: Clog() = default; public: //唯一的创建类的接口 static Clog* createObject() { static Clog p; return &p; } }; int main() { Clog* p = Clog::createObject(); Clog* p1 = Clog::createObject(); }
该方法已经解决了内存释放的问题,但是我们还是可以注意到,他返回的是一个类的指针,既然是指针,难免有同学会一不小心去delete他,运行的时候引起错误。
解决方法------->不去返回一个类的指针了,返回一个类的引用。见第三种单例模式。
#includeusing namespace std; class Clog { private: Clog() = default; public: //唯一的创建类的接口 static Clog& createObject() { static Clog p; return p; } }; int main() { Clog& p = Clog::createObject(); Clog& p1 = Clog::createObject(); }
现在问题已经解决,但是我们又想到,有些同学在main函数中调用这个createObject接口可能会忘了用Clog类型的引用去接,而是用Clog类型的对象去接,例如Clog p2 = Clog::createObject();,这里的p2对象接的就不是我们想要的单例模式的唯一的对象了,而是因为一个“=”号调用了Clog类中的拷贝构造函数,拷贝构造了一个对象,最终的结果就是又生成了一个新对象。
---->解决方法:禁用拷贝构造函数或者禁用”=“运算符重载。见第四种单例模式。
第四种单例模式#includeusing namespace std; class Clog { private: Clog() = default; public: //唯一的创建类的接口 static Clog& createObject() { static Clog p; return p; } Clog(Clog& obj) = delete; Clog* operator= (Clog& obj) = delete;//禁用其中任何一个函数或者都禁用 }; int main() { Clog& p = Clog::createObject(); Clog& p1 = Clog::createObject(); }



