getInstance() 方法中作为静态字段的单例实例与静态变量
在此线程中,有关单例实例的说明如下:
静态变量可以在GetInstance()函数中是静态的,也可以在Singleton类中是静态的。这里有一些有趣的权衡。
这些权衡是什么?我知道,如果声明为静态函数变量,则在首次调用该函数之前不会构造单例。我还读过一些有关线程安全的内容,但我不知道这到底意味着什么,也不知道这两种方法在这方面有何不同。
两者之间还有其他主要区别吗?哪种方法更好?
在我的具体示例中,我将一个工厂类设置为单例,并将实例存储为类中的静态 const 字段。我没有 getInstance()
方法,而是希望用户直接访问实例,如下所示:ItemFactory::factory
。默认构造函数是私有的,并且实例是静态分配的。
附录:重载 operator()
来调用单例的 createItem()
方法是个好主意,这样 Item
就可以了可以像这样创建:ItemFactory::factory("id")
?
In this thread, the following is said about singleton instances:
The static variable can be static to the GetInstance() function, or it can be static in the Singleton class. There's interesting tradeoffs there.
What are these trade-offs? I am aware that, if declared as a static
function variable, the singleton won't be constructed until the function is first called. I've also read something about thread-safety, but am unaware of what exactly that entails, or how the two approaches differ in that regard.
Are there any other major differences between the two? Which approach is better?
In my concrete example, I have a factory class set up as a singleton, and I'm storing the instance as a static const
field in the class. I don't have a getInstance()
method, but rather expect the user to access the instance directly, like so: ItemFactory::factory
. The default constructor is private, and the instance is allocated statically.
Addendum: how good of an idea is it to overload operator()
to call the createItem()
method for the singleton, such that Item
s can be created like so: ItemFactory::factory("id")
?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
这是最重要的考虑因素:
static
数据成员在程序启动时的静态初始化期间被初始化。如果任何static
对象依赖于单例,那么将会有一个静态
初始化顺序惨败。函数本地
静态
对象在函数第一次调用时被初始化。由于依赖单例的人都会调用该函数,因此单例将被适当地初始化并且不会受到失败的影响。破坏仍然存在一个非常微妙的问题。如果静态对象的析构函数依赖于单例,但该对象的构造函数不依赖于单例,那么您最终将得到未定义的行为。另外,在第一次调用函数时初始化,意味着可以在静态初始化完成并且调用
main
后调用该函数。因此,程序可能产生了多个线程。static
本地初始化时可能存在竞争条件,导致构造多个实例。幸运的是,从 C++11 开始,该标准保证初始化是线程安全的,并且这种权衡在符合标准的编译器中不再存在。线程安全不是静态数据成员的问题。
这取决于您的要求以及您支持的标准版本。
This is the most important consideration:
The
static
data member is initialized during the static initialization at the start of the program. If anystatic
object depends on the singleton, then there will be astatic
initialization order fiasco.The function local
static
object is initialized when the function is first called. Since whoever depends on the singleton will call the function, the singleton will be appropriately initialized and is not susceptible to the fiasco. There is still a - very subtle - problem with the destruction. If a destructor of a static object depends on the singleton, but the constructor of that object does not, then you'll end up with undefined behaviour.Also, being initialized on the first time the function is called, means that the function may be called after the static initialization is done and
main
has been called. And therefore, the program may have spawned multiple threads. There could be a race condition on the initialization of thestatic
local, resulting in multiple instances being constructed. Luckily, since C++11, the standard guarantees that the initialization is thread safe and this tradeoff no longer exists in conforming compilers.Thread safety is not an issue with the
static
data member.That depends on what your requirements are and what version of the standard you support.
我投票支持静态函数变量。较新的 C++ 标准要求自动线程安全来初始化此类变量。它在 GNU C++ 中的实现已经有大约十年了。 Visual Studio 2015 也支持这一点。如果您创建一个静态指针变量来保存对单例对象的引用,则必须手动处理线程问题。
另一方面,如果您创建一个静态成员指针字段(如下面的代码片段所示),您将能够从其他静态方法更改它,也许在处理更改程序配置的请求时使用其他实例重新初始化该字段。然而,下面的代码片段包含一个错误,只是为了提醒您多线程是多么困难。
我不建议您将单例实现为在进入
main()
函数之前初始化的全局非指针变量。线程安全问题将随着隐式缓存一致性开销而消失,但您无法以任何精确或可移植的方式控制全局变量的初始化顺序。无论如何,这种选择不会产生任何永久性的设计影响。由于此实例将驻留在类的
private
部分中,因此您可以随时更改它。我认为重载工厂的operator()不是一个好主意。
operator()
具有“执行”语义,而在工厂中它代表“创建”。I vote for static function variable. The newer C++ standard require automatic thread safety for initialization of such variables. It's implemented in GNU C++ for about ten years already. Visual Studio 2015 also supports this. If you make a static pointer variable holding reference to your singleton object, you'll have to deal with thread issues manually.
In the other hand, if you make a static member pointer field like shown in in the snippet below, you will be able to change it from other static methods, maybe re-init this field with other instance upon handling request to change program configuration. However, the snippet below contains a bug just to remind you how difficult multithreading is.
I wouldn't advise you to implement your singleton as a global non-pointer variable initialized before entry to the
main()
function. Thread safety issues will go away along with implicit cache coherency overhead, but you're not able to control the initialization order of your global variables in any precise or portable way.Anyway, this choice doesn't force any permanent design implications. Since this instance will reside in the
private
section of your class you may always change it.I don't think overloading of operator() for a factory is a good idea.
operator()
have "execute" semantics while in factory it's gonna stand for "create".隐藏它是单例的事实并赋予它值语义。
所有的单例性都应该是一个实现细节。这样,如果您需要更改实现单例的方式(或者实际上,如果您决定它毕竟不应该是真正的单例),那么您的类的使用者不需要重构他们的程序。
因为现在你的程序再也不用担心引用、指针、生命周期等等。它只是使用对象的实例,就好像它是一个值一样。安全地知道单例将满足它所具有的任何生命周期/资源需求。
没问题。
下面是隐藏在具有值语义的对象外观后面的两种方法的示例。
想象一下这个用例:
其中一个 jobbie 对象只是单例的一个外观,但是 shared_file 对象希望在不使用时刷新/关闭自身。
所以输出应该如下所示:
我们可以使用惯用语来实现这一点,我将其称为“value-semantics-is-a-facade-for-singleton”:
Hide the fact that it's a singleton and give it value semantics.
All singleton-ness ought to be an implementation detail. In this way, consumers of your class need not refactor their programs if you need to change the way you implement your singleton (or indeed if you decide that it should not really be a singleton after all).
Because now your program never has to worry itself with references, pointers, lifetimes and whatnot. It just uses an instance of the object as if it were a value. Safe in the knowledge that the singleton will take care of whatever lifetime/resource requirements it has.
no problem.
Here's an example of the two approaches hidden behind the facade of an object with value semantics.
imagine this use case:
where a
jobbie
object is just a facade for a singleton, but theshared_file
object wants to flush/close itself when not in use.so the output should look like this:
We can achieve this using the idiom, which I'll call 'value-semantics-is-a-facade-for-singleton':