C++单例模式实例化一个对象不全部使用static的原因是什么
今天小编给大家分享一下C++单例模式实例化一个对象不全部使用static的原因是什么的相关知识点,内容详细,逻辑清晰,相信大部分人都还太了解这方面的知识,所以分享这篇文章给大家参考一下,希望大家阅读完这篇文章后有所收获,下面我们一起来了解一下吧。
C++的单例模式为什么不直接全部使用static,而是非要实例化一个对象?
通过getInstance()函数获取单例对象,这种模式的关键之处不是在于强迫你用函数来获取对象。关键之处是让static对象定义在函数内部,变成局部static变量。
看下这种实现方式的经典demo:
class Singleton {public: static Singleton& getInstance() { static Singleton inst; return inst; } Singleton(const Singleton&) = delete; Singleton& operator=(const Singleton&) = delete; // 其他数据函数 // ...private: Singleton() { ... } // 其他数据成员 // ...};
学名是:Meyers' Singleton。没错,也就是说这是Scott Meyers最早提出来的C++单例模式的推荐写法。
注意:这种单例写法需要C++11。因为是从C++11标准才开始规定 static变量是线程安全的。也就是说无需我们自己写加锁保护的代码,编译器能够帮我们做到。
所以C++程序员们不要在读完Java单例模式的资料之后,在C++程序中写double check或volatile了!
如果是把 static对象定义成 Singleton的私有static成员变量,然后getInstance()去返回这个成员即:
class Singleton {public: static Singleton& getInstance() { return inst; } Singleton(const Singleton&) = delete; Singleton& operator=(const Singleton&) = delete; // 其他数据函数 // ...private: Singleton() { ... } static Singleton inst; // 其他数据成员 // ...};Singleton Singleton::inst;
虽然它也是 先getInstance()再访问,但这种不是Meyers' Singleton!
那么为什么Meyers推荐的是第一种的呢?
原因是这解决了一类重要问题,那就是static变量的初始化顺序的问题。
C++只能保证在同一个文件中声明的static变量的初始化顺序与其变量声明的顺序一致。但是不能保证不同的文件中的static变量的初始化顺序。
然后对于单例模式而言,不同的单例对象之间进行调用也是常见的场景。比如我有一个单例,存储了程序启动时加载的配置文件的内容。另外有一个单例,掌管着一个全局唯一的日志管理器。在日志管理初始化的时候,要通过配置文件的单例对象来获取到某个配置项,实现日志打印。
这时候两个单例在不同文件中各自实现,很有可能在日志管理器的单例使用配置文件单例的时候,配置文件的单例对象是没有被初始化的。这个未初始化可能产生的风险指的是C++变量的未初始化,而不是说配置文件未加载的之类业务逻辑上的未初始化导致的问题。
而Meyers' Singleton写法中,单例对象是第一次访问的时候(也就是第一次调用getInstance()函数的时候)才初始化的,但也是恰恰因为如此,因而能保证如果没有初始化,在该函数调用的时候,是能完成初始化的。所以先getInstance()再访问 这种形式的单例 其关键并不是在于这个形式。而是在于其内容,局部static变量能保证通过函数来获取static变量的时候,该函数返回的对象是肯定完成了初始化的!
讲到这,我们对Meyers' Singleton的盲目鼓吹也需冷静一下,因为C++同样能保证所有文件内(非函数内)的static变量在main()函数开始运行之后肯定是都能做完初始化的。所以如果你是在main()函数运行之后,用日志管理器的单例访问配置文件的单例,那么其实也是没有问题的… 这就引出Meyers' Singleton的第二个优势,那就是当产生继承的时候。
如果出现继承,这种写法中:
class Singleton {public: static void on() {Singleton::isOn = true;} static void off() {Singleton::isOn = false;} static bool state() {return Singleton::isOn;}private: static bool isOn;};class Monitor: public Singleton {public: static void addBrightness(int val) { brightness += val;} static void subBrightness(int val) { brightness -= val;} static int getBrightness() { return brightness;}private: static int brightness;};
如果有子类继承这一父类,来拓展成新的子类,比如Monitor显示器类有开关状态,同时扩展了一个亮度的成员。但是父子类的static成员变量是共享的,其isOn成员会有问题。
好吧,如果你说你的单例完全不会出现继承的情况,是不是就不需要写成Meyers' Singleton?我只想说,如果你一定要强加这么多限定的话,那么这种设计模式的讨论本身就没有意义。就很像是在说:我自己能够保证每个new出来的指针我都能delete掉它,所以我不需要RAII……
以上就是“C++单例模式实例化一个对象不全部使用static的原因是什么”这篇文章的所有内容,感谢各位的阅读!相信大家阅读完这篇文章都有很大的收获,小编每天都会为大家更新不同的知识,如果还想学习更多的知识,请关注编程网行业资讯频道。
免责声明:
① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。
② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341