MyObj* g_singleton; // MyObj is some struct.
MyObj* GetMyObj()
{
MyObj* singleton;
if (g_singleton == NULL)
{
singleton = CreateNewObj();
// Only swap if the existing value is null. If not on Windows,
// use whatever compare and swap your platform provides.
if (InterlockCompareExchangePtr(&g_singleton, singleton, NULL) != NULL)
{
DeleteObj(singleton);
}
}
return g_singleton;
}
DoSomethingWithSingleton(GetMyObj());
7条答案
按热度按时间9q78igpj1#
首先,C不适合OO编程。如果你这样做,你会一直战斗下去。其次,单例只是一些封装的静态变量。所以你可以使用一个静态全局变量。然而,全局变量通常有太多与之相关的弊病。你也可以使用一个函数局部静态变量,像这样:
或者一个更聪明的宏:
最后,请记住,单身人士大多受到虐待。很难让它们正确,特别是在多线程环境下。
cbjzeqam2#
你不需要C已经有了全局变量,所以你不需要一个变通的方法来模拟它们。
hof1towb3#
它与C++版本几乎相同。只需要一个返回示例指针的函数。它可以是函数内部的静态变量。使用临界区或pthread互斥体 Package 函数体,具体取决于平台。
请注意,您还需要一个函数来释放单例。特别是当它抓取任何在进程退出时没有自动释放的系统资源时。
7rfyedvj4#
编辑:我的回答假设你正在创建的单例有点复杂,并且有一个多步骤的创建过程。如果它只是静态数据,就像其他人建议的那样使用全局数据。
C中的单例将非常奇怪。我从来没有见过一个“面向对象的C”的例子看起来特别优雅。如果可能的话,考虑使用C++。C++允许你挑选你想要使用的功能,很多人只是把它当作一个“更好的C”。
下面是一个非常典型的无锁一次性初始化模式。如果前一个值为空,InterlockCompareExchangePtr将自动换入新值。这可以保护如果多个线程试图同时创建单例,只有一个会获胜。其他人将删除他们新创建的对象。
t9aqgxwy5#
这是另一个视角:C程序中的每个文件实际上都是一个在运行时自动示例化的单例类,并且不能被子类化。
extern
声明它们)。给予所有内容一个适当的前缀,现在您可以使用
my_singleton_method()
代替my_singleton.method()
。如果你的单例很复杂,你可以写一个
generate_singleton()
方法在使用前初始化它,但是你需要确保所有其他的公共方法都检查它是否被调用,如果没有,就报错。inn6fuwd6#
我认为这个解决方案可能是最简单和最好的大多数用例.
在本例中,我创建了一个单示例全局调度队列,如果您要跟踪来自多个对象的调度源事件,您肯定会这样做;在这种情况下,当一个新的任务被添加到队列中时,可以通知每个监听队列中事件的对象。一旦设置了全局队列(通过
queue_ref()
),就可以在任何包含头文件的文件中使用queue
变量引用它(下面提供了示例)。在我的一个实现中,我在AppDelegate.m中调用了
queue_ref()
(main.c也可以)。这样,queue
将在任何其他调用对象尝试访问它之前被初始化。在剩下的对象中,我简单地调用了queue
。从变量返回值比调用函数,然后在返回之前检查变量的值要快得多。在GlobalQueue.h中:
在GlobalQueue.c中:
用途:
#include "GlobalQueue.h"
在任何C2C或C实现源文件中。1.调用
queue_ref()
以使用分派队列。一旦调用了queue_ref()
,就可以通过所有源文件中的queue
变量使用队列示例如下:
调用queue_ref():
呼叫队列:
yshpjwxd7#
Just do
这也适用于并发环境。