C++ singleton vs. global static object
我的一个朋友今天问我,为什么他更喜欢使用单例而不是全局静态对象?我开始解释的是,单体可以有状态而不是静态全局对象。但是我不确定。因为这是C++中的…(我来自C)
一个比另一个有什么优势?(在C++中)
实际上,C++中首选的方法是本地静态对象。
1 2 3 4 | Printer & thePrinter() { static Printer printer; return printer; } |
虽然从技术上讲这是一个单例函数,但是这个函数甚至可以是一个类的静态方法。因此,与全局静态对象不同,它保证在使用之前先进行构造,而全局静态对象可以以任何顺序创建,这样,当一个全局对象使用另一个非常常见的场景时,就有可能发生不一致的失败。
通过调用
另一个积极的方面是,在创建singleton之前,甚至从其他静态方法或子类中都无法访问它。为您节省一些调试时间。
在C++中,静态对象在不同编译单元中的实例化顺序是未定义的。因此,一个全局引用另一个未构建的全局是可能的,这会破坏您的程序。单例模式通过将构造绑定到静态成员函数或自由函数来消除这个问题。
这里有一个很好的总结。
型
A friend of mine today asked me why should he prefer use of singleton over global static object? The way I started it to explain was that the singleton can have state vs. static global object won't...but then I wasn't sure..because this in C++.. (I was coming from C#)
号
静态全局对象的状态也可以是C:
1 2 3 4 5 | class myclass { // can have state // ... public static myclass m = new myclass(); // globally accessible static instance, which can have state } |
What are the advantages one over the other? (in C++)
号
单个实例会破坏代码,而全局静态实例则不会。关于独生子女的问题已经有无数的疑问了。这是一个,另一个,或另一个。
简言之,单身汉给你两样东西:
- 百万千克1全局可访问的对象,以及百万千克1百万千克1保证只能创建一个实例。百万千克1
型
如果只需要第一个点,我们应该创建一个全局可访问的对象。为什么我们会想要第二个呢?我们事先不知道我们的代码将来会如何使用,那么为什么要确定它并删除哪些有用的功能呢?当我们预测"我只需要一个实例"时,通常是错误的。"我只需要一个实例"(正确答案是创建一个实例)和"如果创建多个实例,应用程序在任何情况下都无法正常运行"之间有很大的区别。它会崩溃,格式化用户的硬盘,并在互联网上发布敏感数据"(答案是:很可能你的应用程序坏了,但如果不是,那么是的,你需要的是一个单独的应用程序)
与全局静态对象相比,singleton的另一个好处是,由于构造函数是私有的,所以有一个非常明确的编译器强制的指令说"只能有一个"。
相比之下,对于全局静态对象,不会有任何东西阻止开发人员编写创建此对象附加实例的代码。
这个额外约束的好处是,您可以保证如何使用对象。
理由1:单件容易做,所以他们是懒惰的建设。虽然您可以使用Globals来完成这项工作,但开发人员需要额外的工作。因此,默认情况下,全局总是初始化的(除了一些带有名称空间的特殊规则)。
因此,如果您的对象很大并且/或者构建成本很高,您可能不想构建它,除非您真的需要使用它。
理由2:初始化(和销毁)顺序问题。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 | GlobalRes& getGlobalRes() { static GlobalRes instance; // Lazily initialized. return instance; } GlobalResTwo& getGlobalResTwo() { static GlobalResTwo instance; // Lazy again. return instance; } // Order of destruction problem. // The destructor of this object uses another global object so // the order of destruction is important. class GlobalResTwo { public: GlobalResTwo() { getGlobalRes(); // At this point globalRes is fully initialized. // Because it is fully initialized before this object it will be destroyed // after this object is destroyed (Guaranteed) } ~GlobalResTwo() { // It is safe to use globalRes because we know it will not be destroyed // before this object. getGlobalRes().doStuff(); } }; |
使用singleton("construction on first use")习惯用法,可以避免静态初始化顺序失败。
在C++中,两者在实际使用方面没有很大的差别。当然,全局对象可以保持其自身的状态(可能与其他全局变量一起使用,不过我不建议这样做)。如果要使用一个全局或单例(有很多不这样做的原因),在全局对象上使用单例的最大原因是对于单例,可以通过从单例基类继承几个类来实现动态多态性。
好吧,有两个理由去单打真的。一是大家都在谈论的静态顺序问题。
另一种方法是在使用代码时阻止某人执行类似的操作:
1 2 | CoolThing blah; gs_coolGlobalStaticThing = blah; |
或者,更糟的是:
1 | gs_coolGlobalStaticThing = {}; |
封装方面将保护您的实例免受白痴和恶意混蛋的攻击。