繁体   English   中英

C++ 单例 vs. 全局静态对象

[英]C++ singleton vs. global static object

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

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

实际上,在 C++ 中首选的方式是本地静态对象。

Printer & thePrinter() {
    static Printer printer;
    return printer;
}

虽然这在技术上是一个单例,但这个函数甚至可以是一个类的静态方法。 因此,与全局静态对象不同,它保证在使用之前先构造,可以以任何顺序创建,这使得当一个全局对象使用另一个全局对象时可能会不一致地失败,这是一种很常见的情况。

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

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

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

有一个像样的总结在这里

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

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

class myclass {
 // can have state
 // ...
 public static myclass m = new myclass(); // globally accessible static instance, which can have state
}

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

单例会削弱您的代码,而全局静态实例不会。 关于单身人士的问题已经有无数的问题了。 这是一个另一个或另一个

简而言之,单身人士给你两件事:

  • 一个全局可访问的对象,以及
  • 保证只能创建一个实例。

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

Singleton 相对于全局静态对象的另一个好处是,因为构造函数是私有的,所以有一个非常明确的编译器强制指令说“只能有一个”。

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

额外约束的好处是您可以保证如何使用对象。

原因一:
单例很容易制作,所以它们是懒惰的构建。
虽然您可以使用全局变量执行此操作,但需要开发人员进行额外的工作。 所以默认情况下,全局变量总是被初始化(除了一些带有命名空间的特殊规则)。

因此,如果您的对象很大和/或构建起来很昂贵,除非您真的必须使用它,否则您可能不想构建它。

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

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("construct on first use") 习惯用法,可以避免 静态初始化顺序失败

在 C++ 中,就实际用途而言,两者之间没有太大区别。 全局对象当然可以保持自己的状态(可能与其他全局变量一起使用,但我不推荐这样做)。 如果您打算使用全局或单例(并且有很多原因不这样做),在全局对象上使用单例的最大原因是,对于单例,您可以通过从多个类继承来获得动态多态性单例基类。

好吧,确实有两个理由使用单身人士。 一个是每个人都在谈论的静态顺序。

另一个是防止有人在使用你的代码时做这样的事情:

CoolThing blah;
gs_coolGlobalStaticThing = blah;

或者,更糟糕的是:

gs_coolGlobalStaticThing = {};

封装方面将保护您的实例免受白痴和恶意混蛋的侵害。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM