如何在C中创建Singleton?

mznpcxlj  于 12个月前  发布在  其他
关注(0)|答案(7)|浏览(82)

在C中创建单例的最佳方法是什么?一个并行的解决方案会很好。

9q78igpj

9q78igpj1#

首先,C不适合OO编程。如果你这样做,你会一直战斗下去。其次,单例只是一些封装的静态变量。所以你可以使用一个静态全局变量。然而,全局变量通常有太多与之相关的弊病。你也可以使用一个函数局部静态变量,像这样:

int *SingletonInt() {
     static int instance = 42;
     return &instance;
 }

或者一个更聪明的宏:

#define SINGLETON(t, inst, init) t* Singleton_##t() { \
                 static t inst = init;               \
                 return &inst;                       \
                }

#include <stdio.h>  

/* actual definition */
SINGLETON(float, finst, 4.2);

int main() {
    printf("%f\n", *(Singleton_float()));
    return 0;
}

最后,请记住,单身人士大多受到虐待。很难让它们正确,特别是在多线程环境下。

cbjzeqam

cbjzeqam2#

你不需要C已经有了全局变量,所以你不需要一个变通的方法来模拟它们。

hof1towb

hof1towb3#

它与C++版本几乎相同。只需要一个返回示例指针的函数。它可以是函数内部的静态变量。使用临界区或pthread互斥体 Package 函数体,具体取决于平台。

#include <stdlib.h>

struct A {
    int a;
    int b;
};

struct A* getObject() {
    static struct A *instance = NULL;

    // do lock here
    if(instance == NULL)
    {
        instance = malloc(sizeof(*instance));
        instance->a = 1;
        instance->b = 2;
    }
    // do unlock

    return instance;
};

请注意,您还需要一个函数来释放单例。特别是当它抓取任何在进程退出时没有自动释放的系统资源时。

7rfyedvj

7rfyedvj4#

编辑:我的回答假设你正在创建的单例有点复杂,并且有一个多步骤的创建过程。如果它只是静态数据,就像其他人建议的那样使用全局数据。
C中的单例将非常奇怪。我从来没有见过一个“面向对象的C”的例子看起来特别优雅。如果可能的话,考虑使用C++。C++允许你挑选你想要使用的功能,很多人只是把它当作一个“更好的C”。
下面是一个非常典型的无锁一次性初始化模式。如果前一个值为空,InterlockCompareExchangePtr将自动换入新值。这可以保护如果多个线程试图同时创建单例,只有一个会获胜。其他人将删除他们新创建的对象。

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());
t9aqgxwy

t9aqgxwy5#

这是另一个视角:C程序中的每个文件实际上都是一个在运行时自动示例化的单例类,并且不能被子类化。

  • 全局静态变量是你的私有类成员。
  • 全局非静态是公共的(只需在某些头文件中使用extern声明它们)。
  • 静态函数是私有方法
  • 非静态函数是公共函数。

给予所有内容一个适当的前缀,现在您可以使用my_singleton_method()代替my_singleton.method()
如果你的单例很复杂,你可以写一个generate_singleton()方法在使用前初始化它,但是你需要确保所有其他的公共方法都检查它是否被调用,如果没有,就报错。

inn6fuwd

inn6fuwd6#

我认为这个解决方案可能是最简单和最好的大多数用例.
在本例中,我创建了一个单示例全局调度队列,如果您要跟踪来自多个对象的调度源事件,您肯定会这样做;在这种情况下,当一个新的任务被添加到队列中时,可以通知每个监听队列中事件的对象。一旦设置了全局队列(通过queue_ref()),就可以在任何包含头文件的文件中使用queue变量引用它(下面提供了示例)。
在我的一个实现中,我在AppDelegate.m中调用了queue_ref()(main.c也可以)。这样,queue将在任何其他调用对象尝试访问它之前被初始化。在剩下的对象中,我简单地调用了queue。从变量返回值比调用函数,然后在返回之前检查变量的值要快得多。
在GlobalQueue.h中:

#ifndef GlobalQueue_h
#define GlobalQueue_h

#include <stdio.h>
#include <dispatch/dispatch.h>

extern dispatch_queue_t queue;

extern dispatch_queue_t queue_ref(void);

#endif /* GlobalQueue_h */

在GlobalQueue.c中:

#include "GlobalQueue.h"

dispatch_queue_t queue;

dispatch_queue_t queue_ref(void) {
    if (!queue) {
        queue = dispatch_queue_create_with_target("GlobalDispatchQueue", DISPATCH_QUEUE_SERIAL, dispatch_get_main_queue());
    }
    
    return queue;
}

用途:

  1. #include "GlobalQueue.h"在任何C2C或C实现源文件中。
    1.调用queue_ref()以使用分派队列。一旦调用了queue_ref(),就可以通过所有源文件中的queue变量使用队列
    示例如下:
    调用queue_ref():
dispatch_queue_t serial_queue_with_queue_target = dispatch_queue_create_with_target("serial_queue_with_queue_target", DISPATCH_QUEUE_SERIAL, **queue_ref()**);

呼叫队列:

dispatch_queue_t serial_queue_with_queue_target = dispatch_queue_create_with_target("serial_queue_with_queue_target", DISPATCH_QUEUE_SERIAL, **queue**));]
yshpjwxd

yshpjwxd7#

Just do

void * getSingleTon() {
    static Class object = (Class *)malloc( sizeof( Class ) );
    return &object;
}

这也适用于并发环境。

相关问题