C++ 单例 vs 完全静态对象
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/3841034/
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
C++ singleton vs completely static object
提问by Victor Ronin
Let say we need to have just one instance of some class in our project. There are couple ways of doing it.
假设我们的项目中只需要某个类的一个实例。有几种方法可以做到这一点。
I want to compare. Please can you review my understanding.
我想比较。请您检查一下我的理解。
1) Classical Singleton pattern
1) 经典的单例模式
2) Completely static class (all methods and members are static).
2) 完全静态的类(所有方法和成员都是静态的)。
As I understand the differences are following:
据我了解,差异如下:
a) The order of initialization of static members across different units isn't defined. So, completely static members initialization can't use any static members/functions from other modules. And singleton doesn't have this problem.
a) 不同单元的静态成员的初始化顺序没有定义。因此,完全静态成员初始化不能使用来自其他模块的任何静态成员/函数。而单身人士则没有这个问题。
b) We have to deal with threading for getInstance() of Singleton. However, completely static class doesn't have this problem.
b) 我们必须为 Singleton 的 getInstance() 处理线程。但是,完全静态的类没有这个问题。
c) Access to methods looks a little bit different. Foo::bar(); vs Foo::getInstance()->bar(); Generally, singleton can return NULL to identify that there were some problems with construction of object and static class can't.
c) 对方法的访问看起来有点不同。Foo::bar(); vs Foo::getInstance()->bar(); 一般情况下,singleton 可以返回 NULL 来标识对象的构造有问题,而静态类则不能。
d) Definition of class looks a little bit clunky with bunch of statics for static class.
d)类的定义看起来有点笨拙,静态类有一堆静态。
Have I missed anything?
我错过了什么吗?
回答by Matthieu M.
Whether you call it Singleton or Monostate or any fancy name... the very annoying nature of this is that you have ONE instance of the object and many writes to it: global variables, whatever their guise, are evil.
无论你称它为 Singleton 还是 Monostate 或任何花哨的名字......这个非常烦人的本质是你有一个对象的实例并且许多写入它:全局变量,无论它们的伪装如何,都是邪恶的。
The idea that you needa unique instance is generally clunky. Most of the times what you really need is parts that communicate share the same instance. But another group of parts could perfectly use another instance without issue.
您需要一个唯一实例的想法通常很笨拙。大多数时候,您真正需要的是共享相同实例的通信部件。但是另一组零件可以完美地使用另一个实例而不会出现问题。
Any code that claim to needa global variable is highly suspicious. It may appear simpler to use one, but let's face it, you could perfectly pass the object to each and every function, it would complicate their signature but it would work nonetheless.
任何声称需要全局变量的代码都是高度可疑的。使用它可能看起来更简单,但让我们面对现实,您可以完美地将对象传递给每个函数,这会使它们的签名复杂化,但它仍然可以工作。
However, I admit, it appears simpler to use global variables... until you notice the issues:
但是,我承认,使用全局变量似乎更简单……直到您注意到问题:
- multithreadingis compromised
- testabilityis reduced, since one test may affect the one following it
- dependency analysis is extremely complicated: it's hard to know what state your method depend on when you pull in global from within submethods...
- 多线程受到损害
- 可测试性降低,因为一项测试可能会影响接下来的一项
- 依赖分析非常复杂:当你从子方法中拉入全局时,很难知道你的方法依赖什么状态......
Now, as far as singleton is concerned, multithreaded creation is not usable in C++ before C++0x (when it becomes possible using static locals), thus you need to create it in only one thread and delay access before: instantiate it in main, it's your best bet.
现在,就单例而言,在 C++0x 之前的 C++ 中无法使用多线程创建(当可以使用静态局部变量时),因此您只需要在一个线程中创建它并延迟访问之前:在 main 中实例化它,这是你最好的选择。
Destruction may cause mayhem since the life of the Singleton / Static may end before others are done with it, and then it's undefined behavior. This is typical of a Logger
singleton. The usual strategy is to shamelessly leak...
破坏可能会导致混乱,因为 Singleton / Static 的生命可能会在其他人完成之前结束,然后它是未定义的行为。这是典型的Logger
单身人士。通常的策略是无耻地泄漏......
After that, if you still want one, I wish you good luck, that's all this community can do for you.
之后,如果你还想要一个,祝你好运,这就是这个社区能为你做的一切。
回答by Evan Teran
Another option you overlook is namespace
's.
您忽略的另一个选项是namespace
's。
namespace xyz {
namespace {
int private_variable;
}
int get_pv() {
return private_variable;
}
}
Functionally, this is going to be similar to your option #2, but you can't accidentally "delete" this thing. You can't accidentally create an instance of it. It is just a collection of related globally accessible data and functions. You can (as in my example) even have "private" members and functions.
从功能上讲,这将类似于您的选项 #2,但您不能意外地“删除”这个东西。您不能意外地创建它的实例。它只是相关的全局可访问数据和函数的集合。您甚至可以(如我的示例)拥有“私有”成员和功能。
Of course the usage would be something like this:
当然用法是这样的:
int x = xyz::get_pv();
回答by Martin York
Let say we need to have just one instance of some class in our project. There are couple ways of doing it.
假设我们的项目中只需要某个类的一个实例。有几种方法可以做到这一点。
A better solution:
更好的解决方案:
A variable in main that you pass as a parameter to all required functions would be another.
作为参数传递给所有必需函数的 main 变量将是另一个变量。
a) The order of initialization of static members across different units isn't defined. So, completely static members initialization can't use any static members/functions from other modules. And singleton doesn't have this problem.
a) 不同单元的静态成员的初始化顺序没有定义。因此,完全静态成员初始化不能使用来自其他模块的任何静态成员/函数。而单身人士则没有这个问题。
Singletons do have this problem if their constructor/destructor access other global static lifespan variable.
如果它们的构造函数/析构函数访问其他全局静态生命周期变量,单身人士确实有这个问题。
b) We have to deal with threading for getInstance() of Sigleton. However, completely static class doesn't have this problem.
b) 我们必须为 Sigleton 的 getInstance() 处理线程。但是,完全静态的类没有这个问题。
Not really a problem is it? If you are aware of it just add the appropriate locks in the code.
真的不是问题吗?如果您知道它,只需在代码中添加适当的锁即可。
c) Access to methods looks a little bit different. Foo::bar(); vs Foo::getInstance()->bar(); Generally, sigleton can return NULL to identify that there were some problems with construction of object and static class can't.
c) 对方法的访问看起来有点不同。Foo::bar(); vs Foo::getInstance()->bar(); 一般情况下,sigleton 可以返回 NULL 来标识对象的构造有问题,而静态类则不能。
I would make my getInstance() return a reference. Then there is no ambiguity over if the pointer is NULL. It either worked or threw an exception. Also this leads to a design where the destruction is correct called on the instance (Don't take this as advice to use Singleton I would avoid it if possible (but if you do use make it neat)).
我会让我的 getInstance() 返回一个引用。如果指针为 NULL,则没有歧义。它要么工作要么抛出异常。这也导致了在实例上正确调用销毁的设计(不要将此作为使用单例的建议,如果可能,我会避免使用它(但如果您确实使用使其整洁))。
d) Definition of class looks a little bit clunky with bunch of statics for static class.
d)类的定义看起来有点笨拙,静态类有一堆静态。
No clunkier than writing a singleton properly.
没有比正确编写单例更笨拙的了。
The problem with both these methods is that they are both accessing global mutable state
and thus the use of these 'single instance' objects by other objects is hidden from the user. This can lead to problems with testing (TDD requires the ability to mock external functionality but global mutable state
prevents the ability of the tester from mocking external dependencies (easily)).
这两种方法的问题在于它们都在访问global mutable state
,因此其他对象对这些“单实例”对象的使用对用户是隐藏的。这可能会导致测试问题(TDD 需要能够模拟外部功能,但会global mutable state
阻止测试人员模拟外部依赖项的能力(轻松))。
Any object that is non POD has a constructor that may potentially throw an exception. Thus for objects in the global namespace this means that exceptions can be thrown before main() is entered (this can lead to hard to find bugs (if you have a lot of global objects (you have to put breakpoints everywhere)). But the same problem exists with a singleton that is lazily evaluated; If on first use it throws how do you correct for this so that a subsequent attempt does not throw? Ow will your application continue throwing each time the singleton is retrieved?
任何非 POD 的对象都有一个可能引发异常的构造函数。因此,对于全局命名空间中的对象,这意味着可以在进入 main() 之前抛出异常(这可能导致难以找到错误(如果您有很多全局对象(您必须在各处放置断点))。但是懒惰评估的单例也存在同样的问题;如果第一次使用它抛出你如何纠正这个问题,以便随后的尝试不会抛出?每次检索到单例时,你的应用程序会继续抛出吗?
回答by Colin
You could also use the Borg pattern, which is a little more complicated in C++ unless you have access to a shared pointer class. The idea is that any number of Borg classes can be instantiated, but their state is shared across all instances.
您还可以使用 Borg 模式,除非您可以访问共享指针类,否则它在 C++ 中会稍微复杂一些。这个想法是可以实例化任意数量的 Borg 类,但它们的状态在所有实例之间共享。
回答by Jay
You might add: static objects can throw exceptions. The executable will not to start and it's difficult to debug/handle well.
您可能会添加:静态对象可以抛出异常。可执行文件不会启动,并且很难调试/处理好。