C++ 单例:应该如何使用

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/86582/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-27 12:44:38  来源:igfitidea点击:

Singleton: How should it be used

c++design-patternssingleton

提问by Martin York

Edit: From another question I provided an answer that has links to a lot of questions/answers about singletons: More info about singletons here:

编辑:从另一个问题我提供了一个答案,其中包含指向许多关于单身人士的问题/答案的链接:有关单身人士的更多信息在这里:

So I have read the thread Singletons: good design or a crutch?
And the argument still rages.

所以我已经阅读了单身人士:好的设计还是拐杖?
争论仍然激烈。

I see Singletons as a Design Pattern (good and bad).

The problem with Singleton is not the Pattern but rather the users (sorry everybody). Everybody and their father thinks they can implement one correctly (and from the many interviews I have done, most people can't). Also because everybody thinks they can implement a correct Singleton they abuse the Pattern and use it in situations that are not appropriate (replacing global variables with Singletons!).

我认为单例是一种设计模式(好的和坏的)。

Singleton 的问题不在于模式,而在于用户(对不起大家)。每个人和他们的父亲都认为他们可以正确地实施一个(从我所做的许多采访来看,大多数人不能)。也因为每个人都认为他们可以实现一个正确的单例,他们滥用模式并在不合适的情况下使用它(用单例替换全局变量!)。

So the main questions that need to be answered are:

所以需要回答的主要问题是:

  • When should you use a Singleton
  • How do you implement a Singleton correctly
  • 什么时候应该使用单例
  • 你如何正确实现单例

My hope for this article is that we can collect together in a single place (rather than having to google and search multiple sites) an authoritative source of when (and then how) to use a Singleton correctly. Also appropriate would be a list of Anti-Usages and common bad implementations explaining why they fail to work and for good implementations their weaknesses.

我对这篇文章的希望是,我们可以在一个地方(而不是必须通过谷歌搜索多个站点)收集有关何时(以及如何)正确使用 Singleton 的权威来源。同样合适的是反用法和常见不良实现的列表,解释为什么它们无法工作以及良好的实现它们的弱点。



So get the ball rolling:
I will hold my hand up and say this is what I use but probably has problems.
I like "Scott Myers" handling of the subject in his books "Effective C++"

所以让球滚动:
我会举起手说这是我使用的但可能有问题。
我喜欢“Scott Myers”在他的“Effective C++”一书中对这个主题的处理

Good Situations to use Singletons (not many):

  • Logging frameworks
  • Thread recycling pools

使用单例的好情况(不多):

  • 日志框架
  • 线程回收池
/*
 * C++ Singleton
 * Limitation: Single Threaded Design
 * See: http://www.aristeia.com/Papers/DDJ_Jul_Aug_2004_revised.pdf
 *      For problems associated with locking in multi threaded applications
 *
 * Limitation:
 * If you use this Singleton (A) within a destructor of another Singleton (B)
 * This Singleton (A) must be fully constructed before the constructor of (B)
 * is called.
 */
class MySingleton
{
    private:
        // Private Constructor
        MySingleton();
        // Stop the compiler generating methods of copy the object
        MySingleton(MySingleton const& copy);            // Not Implemented
        MySingleton& operator=(MySingleton const& copy); // Not Implemented

    public:
        static MySingleton& getInstance()
        {
            // The only instance
            // Guaranteed to be lazy initialized
            // Guaranteed that it will be destroyed correctly
            static MySingleton instance;
            return instance;
        }
};

OK. Lets get some criticism and other implementations together.
:-)

好的。让我们把一些批评和其他实现放在一起。
:-)

采纳答案by Javaxpert

All of you are wrong. Read the question. Answer:

你们都错了。阅读问题。回答:

Use a Singleton if:

在以下情况下使用单例:

  • You need to have one and only one object of a type in system
  • 您需要在系统中拥有一个且只有一个类型的对象

Do not use a Singleton if:

在以下情况下不要使用单例:

  • You want to save memory
  • You want to try something new
  • You want to show off how much you know
  • Because everyone else is doing it (See cargo cult programmerin wikipedia)
  • In user interface widgets
  • It is supposed to be a cache
  • In strings
  • In Sessions
  • I can go all day long
  • 你想节省内存
  • 你想尝试新的东西
  • 你想炫耀你知道多少
  • 因为其他人都在这样做(请参阅维基百科中的货物崇拜程序员
  • 在用户界面小部件中
  • 应该是缓存
  • 在字符串中
  • 在会话中
  • 我可以走一整天

How to create the best singleton:

如何创建最好的单身人士:

  • The smaller, the better. I am a minimalist
  • Make sure it is thread safe
  • Make sure it is never null
  • Make sure it is created only once
  • Lazy or system initialization? Up to your requirements
  • Sometimes the OS or the JVM creates singletons for you (e.g. in Java every class definition is a singleton)
  • Provide a destructor or somehow figure out how to dispose resources
  • Use little memory
  • 越小越好。我是极简主义者
  • 确保它是线程安全的
  • 确保它永远不会为空
  • 确保它只创建一次
  • 懒惰或系统初始化?满足您的要求
  • 有时操作系统或 JVM 会为您创建单例(例如,在 Java 中,每个类定义都是单例)
  • 提供析构函数或以某种方式弄清楚如何处理资源
  • 使用很少的内存

回答by jalf

Singletons give you the ability to combine two bad traits in one class. That's wrong in pretty much every way.

单身使您能够在一个班级中结合两个不良特征。这几乎在所有方面都是错误的。

A singleton gives you:

单身人士给你:

  1. Global access to an object, and
  2. A guarantee that no more than one object of this type can ever be created
  1. 对对象的全局访问,以及
  2. 保证不能创建超过一个这种类型的对象

Number one is straightforward. Globals are generally bad. We should never make objects globally accessible unless we reallyneed it.

第一个是直截了当的。全局通常是坏的。除非我们真的需要,否则我们永远不应该让对象全局可访问。

Number two may sound like it makes sense, but let's think about it. When was the last time you **accidentally* created a new object instead of referencing an existing one? Since this is tagged C++, let's use an example from that language. Do you often accidentally write

第二个可能听起来有道理,但让我们考虑一下。你最后一次**意外*创建一个新对象而不是引用一个现有对象是什么时候?由于这是标记为 C++,让我们使用该语言的示例。你是不是经常不小心写

std::ostream os;
os << "hello world\n";

When you intended to write

当你打算写

std::cout << "hello world\n";

Of course not. We don't need protection against this error, because that kind of error just doesn't happen. If it does, the correct response is to go home and sleep for 12-20 hours and hope you feel better.

当然不是。我们不需要针对此错误的保护,因为这种错误不会发生。如果是这样,正确的反应是回家睡 12-20 个小时,希望您感觉好些。

If only one object is needed, simply create one instance. If one object should be globally accessible, make it a global. But that doesn't mean it should be impossible to create other instances of it.

如果只需要一个对象,只需创建一个实例。如果一个对象应该是全局可访问的,请将其设为全局对象。但这并不意味着创建它的其他实例是不可能的。

The "only one instance is possible" constraint doesn't really protect us against likely bugs. But it doesmake our code very hard to refactor and maintain. Because quite often we find out laterthat we did need more than one instance. We dohave more than one database, we dohave more than one configuration object, we do want several loggers. Our unit tests may want to be able to create and recreate these objects every test, to take a common example.

“只有一个实例是可能的”约束并不能真正保护我们免受可能的错误的影响。但它确实使我们的代码很难重构和维护。因为很多时候我们后来发现我们确实需要多个实例。我们确实有多个数据库,我们确实有多个配置对象,我们确实需要多个记录器。我们的单元测试可能希望能够在每个测试中创建和重新创建这些对象,举一个常见的例子。

So a singleton should be used if and only if, we need boththe traits it offers: If we needglobal access (which is rare, because globals are generally discouraged) andwe needto prevent anyone from evercreating more than one instance of a class (which sounds to me like a design issue). The only reason I can see for this is if creating two instances would corrupt our application state - probably because the class contains a number of static members or similar silliness. In which case the obvious answer is to fix that class. It shouldn't depend on being the only instance.

因此,一个单应当且仅当被使用,我们需要这两个它提供的特性:如果我们需要全球性的访问(这是罕见的,因为全局一般气馁)我们需要防止任何人曾经创造的多个实例类(这听起来像一个设计问题)。我能看到的唯一原因是创建两个实例是否会破坏我们的应用程序状态 - 可能是因为该类包含许多静态成员或类似的愚蠢行为。在这种情况下,显而易见的答案是修复该类。它不应该依赖于成为唯一的实例。

If you need global access to an object, make it a global, like std::cout. But don't constrain the number of instances that can be created.

如果您需要对某个对象进行全局访问,请将其设为全局,例如std::cout. 但不要限制可以创建的实例数量。

If you absolutely, positively need to constrain the number of instances of a class to just one, and there is no way that creating a second instance can ever be handled safely, then enforce that. But don't make it globally accessible as well.

如果您绝对肯定需要将一个类的实例数量限制为一个,并且无法安全地创建第二个实例,那么请强制执行。但也不要让它在全球范围内都可以访问。

If you do need both traits, then 1) make it a singleton, and 2) let me know what you need that for, because I'm having a hard time imagining such a case.

如果你确实需要这两个特征,那么 1) 让它成为一个单例,2) 让我知道你需要它做什么,因为我很难想象这样的情况。

回答by DrPizza

The problem with singletons is not their implementation. It is that they conflate two different concepts, neither of which is obviously desirable.

单例的问题不在于它们的实现。正是它们将两个不同的概念混为一谈,显然这两个概念都不是可取的。

1) Singletons provide a global access mechanism to an object. Although they might be marginally more threadsafe or marginally more reliable in languages without a well-defined initialization order, this usage is still the moral equivalent of a global variable. It's a global variable dressed up in some awkward syntax (foo::get_instance() instead of g_foo, say), but it serves the exact same purpose (a single object accessible across the entire program) and has the exact same drawbacks.

1) 单例提供了一个对象的全局访问机制。尽管在没有明确定义的初始化顺序的语言中,它们可能稍微更线程安全或更可靠,但这种用法仍然是全局变量的道德等价物。它是一个用一些笨拙的语法装饰的全局变量(比如 foo::get_instance() 而不是 g_foo),但它服务于完全相同的目的(在整个程序中可以访问单个对象)并且具有完全相同的缺点。

2) Singletons prevent multiple instantiations of a class. It's rare, IME, that this kind of feature should be baked into a class. It's normally a much more contextual thing; a lot of the things that are regarded as one-and-only-one are really just happens-to-be-only-one. IMO a more appropriate solution is to just create only one instance--until you realize that you need more than one instance.

2) 单例防止一个类的多个实例化。在 IME 中,这种功能很少会被整合到一个类中。这通常是一个更加上下文的事情;很多被认为是唯一的事情实际上只是碰巧是唯一的。IMO 更合适的解决方案是只创建一个实例——直到您意识到您需要多个实例。

回答by Pawe? Hajdan

One thing with patterns: don't generalize. They have all cases when they're useful, and when they fail.

模式的一件事:不要一概而论。他们有所有有用的情况,也有失败的情况。

Singleton can be nasty when you have to testthe code. You're generally stuck with one instance of the class, and can choose between opening up a door in constructor or some method to reset the state and so on.

当您必须测试代码时,单例可能会令人讨厌。您通常只能使用类的一个实例,并且可以在构造函数中打开一扇门或某些方法来重置状态等之间进行选择。

Other problem is that the Singleton in fact is nothing more than a global variablein disguise. When you have too much global shared state over your program, things tend to go back, we all know it.

另一个问题是,Singleton 实际上只不过是一个变相的全局变量。当你的程序有太多的全局共享状态时,事情往往会倒退,我们都知道。

It may make dependency trackingharder. When everything depends on your Singleton, it's harder to change it, split to two, etc. You're generally stuck with it. This also hampers flexibility. Investigate some Dependency Injectionframework to try to alleviate this issue.

它可能会使依赖性跟踪更加困难。当一切都取决于你的单身人士时,改变它、拆分为两个等等就更难了。你通常会坚持下去。这也妨碍了灵活性。研究一些依赖注入框架来尝试缓解这个问题。

回答by Eli Courtwright

Singletons basically let you have complex global state in languages which otherwise make it difficult or impossible to have complex global variables.

单例基本上让你在语言中拥有复杂的全局状态,否则很难或不可能拥有复杂的全局变量。

Java in particular uses singletons as a replacement for global variables, since everything must be contained within a class. The closest it comes to global variables are public static variables, which may be used as if they were global with import static

Java 特别使用单例作为全局变量的替代品,因为一切都必须包含在一个类中。最接近全局变量的是公共静态变量,它们可以像全局变量一样使用import static

C++ does have global variables, but the order in which constructors of global class variables are invoked is undefined. As such, a singleton lets you defer the creation of a global variable until the first time that variable is needed.

C++ 确实有全局变量,但调用全局类变量的构造函数的顺序是未定义的。因此,单例允许您将全局变量的创建推迟到第一次需要该变量时。

Languages such as Python and Ruby use singletons very little because you can use global variables within a module instead.

Python 和 Ruby 等语言很少使用单例,因为您可以在模块中使用全局变量。

So when is it good/bad to use a singleton? Pretty much exactly when it would be good/bad to use a global variable.

那么什么时候使用单例好/坏呢?几乎确切地何时使用全局变量是好/坏。

回答by Mark Ransom

  • How do you implement a Singleton correctly
  • 你如何正确实现单例

There's one issue I've never seen mentioned, something I ran into at a previous job. We had C++ singletons that were shared between DLLs, and the usual mechanics of ensuring a single instance of a class just don't work. The problem is that each DLL gets its own set of static variables, along with the EXE. If your get_instance function is inline or part of a static library, each DLL will wind up with its own copy of the "singleton".

有一个我从未见过提到的问题,这是我在上一份工作中遇到的问题。我们有在 DLL 之间共享的 C++ 单例,并且确保类的单个实例的通常机制不起作用。问题是每个 DLL 都有自己的一组静态变量,以及 EXE。如果您的 get_instance 函数是内联函数或静态库的一部分,则每个 DLL 都会以自己的“单例”副本结束。

The solution is to make sure the singleton code is only defined in one DLL or EXE, or create a singleton manager with those properties to parcel out instances.

解决方案是确保单例代码仅在一个 DLL 或 EXE 中定义,或者创建具有这些属性的单例管理器来分配实例。

回答by tenpn

Modern C++ Designby Alexandrescu has a thread-safe, inheritable generic singleton.

Alexandrescu 的现代 C++ 设计具有线程安全、可继承的通用单例。

For my 2p-worth, I think it's important to have defined lifetimes for your singletons (when it's absolutely necessary to use them). I normally don't let the static get()function instantiate anything, and leave set-up and destruction to some dedicated section of the main application. This helps highlight dependencies between singletons - but, as stressed above, it's best to just avoid them if possible.

对于我的 2p 价值,我认为为你的单身人士定义生命周期很重要(当绝对有必要使用它们时)。我通常不会让静态get()函数实例化任何东西,而将设置和销毁留给主应用程序的某些专用部分。这有助于突出单例之间的依赖关系 - 但是,如上所述,最好尽可能避免它们。

回答by tenpn

As others have noted, major downsides to singletons include the inability to extend them, and losing the power to instantiate more than one instance, e.g. for testing purposes.

正如其他人所指出的,单例的主要缺点包括无法扩展它们,以及失去实例化多个实例的能力,例如用于测试目的。

Some useful aspects of singletons:

单身人士的一些有用方面:

  1. lazy or upfront instantiation
  2. handy for an object which requires setup and/or state
  1. 懒惰或预先实例化
  2. 对于需要设置和/或状态的对象很方便

However, you don't have to use a singleton to get these benefits. You can write a normal object that does the work, and then have people access it via a factory (a separate object). The factory can worry about only instantiating one, and reusing it, etc., if need be. Also, if you program to an interface rather than a concrete class, the factory can use strategies, i.e. you can switch in and out various implementations of the interface.

但是,您不必使用单例来获得这些好处。您可以编写一个正常的对象来完成工作,然后让人们通过工厂(一个单独的对象)访问它。如果需要,工厂可以担心只实例化一个,然后重用它等等。此外,如果您针对接口而不是具体类进行编程,则工厂可以使用策略,即您可以切入和切出接口的各种实现。

Finally, a factory lends itself to dependency injection technologies like Spring etc.

最后,工厂适用于 Spring 等依赖注入技术。

回答by Rob

The first example isn't thread safe - if two threads call getInstance at the same time, that static is going to be a PITA. Some form of mutex would help.

第一个示例不是线程安全的 - 如果两个线程同时调用 getInstance,则该静态将成为 PITA。某种形式的互斥锁会有所帮助。

回答by Brian

Singletons are handy when you've got a lot code being run when you initialize and object. For example, when you using iBatis when you setup a persistence object it has to read all the configs, parse the maps, make sure its all correct, etc.. before getting to your code.

当您在初始化和对象时运行大量代码时,单例很方便。例如,当您在设置持久性对象时使用 iBatis 时,它必须读取所有配置、解析映射、确保其全部正确等,然后才能访问您的代码。

If you did this every time, performance would be much degraded. Using it in a singleton, you take that hit once and then all subsequent calls don't have to do it.

如果你每次都这样做,性能会大大降低。在单例中使用它,您只需一次命中,然后所有后续调用都不必这样做。