C++ C++;单例与全局静态对象

C++ C++;单例与全局静态对象,c++,static,singleton,global-variables,C++,Static,Singleton,Global Variables,今天我的一个朋友问我为什么他更喜欢使用单例而不是全局静态对象? 我开始解释的是,单体可以有状态与静态全局对象不…但是我不确定…因为这在C++中…(我来自C#) 一个比另一个有什么优势?(C++中) C++中,静态对象在不同编译单元中的实例化顺序是未定义的。因此,有可能一个全局引用另一个未构建的全局,从而破坏您的程序。singleton模式通过将构造绑定到静态成员函数或自由函数来消除这个问题 有一个不错的总结。 < P>实际上,C++中首选的方法是本地静态对象。 Printer & the

今天我的一个朋友问我为什么他更喜欢使用单例而不是全局静态对象? 我开始解释的是,单体可以有状态与静态全局对象不…但是我不确定…因为这在C++中…(我来自C#)


一个比另一个有什么优势?(C++中)

C++中,静态对象在不同编译单元中的实例化顺序是未定义的。因此,有可能一个全局引用另一个未构建的全局,从而破坏您的程序。singleton模式通过将构造绑定到静态成员函数或自由函数来消除这个问题


<>有一个不错的总结。

< P>实际上,C++中首选的方法是本地静态对象。
Printer & thePrinter() {
    static Printer printer;
    return printer;
}
从技术上讲,这是一个单例函数,这个函数甚至可以是一个类的静态方法。因此,它保证在使用之前先构造,不像全局静态对象那样,全局静态对象可以以任何顺序创建,这使得当一个全局对象使用另一个全局对象时,可能会出现不一致的失败,这是一种非常常见的情况

与通过调用
new
创建新实例的常用方法相比,它的优点是在程序结束时调用对象析构函数。动态分配的单例不会发生这种情况

另一个积极的方面是在创建singleton之前无法访问它,即使是从其他静态方法或子类。为您节省一些调试时间。

原因1:
单件很容易制作,因此它们是惰性构建。
虽然您可以使用globals实现这一点,但开发人员需要额外的工作。因此,默认情况下,全局变量总是初始化的(除了一些具有名称空间的特殊规则)

因此,如果您的对象很大并且/或者构建成本很高,您可能不想构建它,除非您真的必须使用它

原因二:
初始化(和销毁)顺序问题


使用Singleton(“第一次使用时构造”)习惯用法,可以避免

与全局静态对象相比,Singleton的另一个好处是,由于构造函数是私有的,因此有一个非常明确的、编译器强制执行的指令说“只能有一个”

相比之下,与全局静态对象相比,没有什么可以阻止开发人员编写代码来创建该对象的附加实例


<> P>额外约束的好处是,你有一个关于对象如何使用的保证。C++中的

,在实际使用方面,两者之间没有很大的差别。一个全局对象当然可以保持它自己的状态(可能与其他全局变量一起,尽管我不推荐)。如果要使用全局或单例(有很多理由不这样做),在全局对象上使用单例的最大原因是,对于单例,可以通过让多个类继承自单例基类来实现动态多态性。

好的,使用单例有两个理由。一个是每个人都在谈论的静态秩序

另一种方法是在使用您的代码时防止有人这样做:

CoolThing blah;
gs_coolGlobalStaticThing = blah;
或者,更糟糕的是:

gs_coolGlobalStaticThing = {};
封装方面将保护您的实例免受白痴和恶意的攻击

今天我的一个朋友问我为什么他更喜欢使用单例而不是全局静态对象?我开始解释的是,单体可以有状态与静态全局对象不…但是我不确定…因为这在C++中…(我来自C#)

静态全局对象也可以在C#中具有状态:

一个比另一个有什么优势?(在C++中)

单例会破坏代码,而全局静态实例则不会。 关于单身汉的问题已经有无数的问题了

简言之,单身人士给你两样东西:

  • 全局可访问对象,以及
  • 只能创建一个实例的保证
如果我们只需要第一个点,我们应该创建一个全局可访问的对象。
为什么我们会想要第二个呢?我们事先不知道我们的代码将来会如何使用,所以为什么要把它固定下来,删除可能有用的功能呢?当我们预测“我只需要一个实例”时,我们通常是错的。“我只需要一个实例”(正确的答案是创建一个实例)和“如果创建了多个实例,应用程序在任何情况下都无法正常运行。它会崩溃,格式化用户的硬盘,并在互联网上发布敏感数据”(这里的答案是:很可能你的应用程序坏了,但如果它没有坏,那么是的,你需要一个单例)

这是制作单例的最好方法。使用新的在实际代码中并不常见,尽管互联网上有很多例子似乎表明这是一个好主意(不要相信他们)。实际上,没有“最好的”生成单例的方法。如果此单例在其析构函数中引用了另一个单例,它将无法正常工作。请注意,这通常不是线程安全的(取决于实现:例如,g++将插入锁,但MSVC不会)。如果你的单例引用了另一个单例,那么你应该被禁止再写一行代码。当然,你也不能再写更多的单例。然后你陷入了1970年代的思维模式,所有数据都必须是全局的……然后你可以通过使构造函数private并使printer()成为朋友函数。那篇文章很糟糕。提出的解决方案和问题一样糟糕。通过在函数中使用new,您现在将永远不会调用析构函数。这是极少数内存泄漏可以解决的情况之一,因为程序无论如何都会结束(除非你使用的是一个非常糟糕的操作系统,它无法回收内存)
gs_coolGlobalStaticThing = {};
class myclass {
 // can have state
 // ...
 public static myclass m = new myclass(); // globally accessible static instance, which can have state
}