Was ist der beste Weg, um ein Singleton in C zu erstellen? Eine parallele Lösung wäre schön.
Mir ist bewusst, dass C nicht die erste Sprache ist, die Sie für einen Singleton verwenden würden.
Liran Orevi
Was ist der beste Weg, um ein Singleton in C zu erstellen? Eine parallele Lösung wäre schön.
Mir ist bewusst, dass C nicht die erste Sprache ist, die Sie für einen Singleton verwenden würden.
dirkly
Erstens ist C nicht für die OO-Programmierung geeignet. Du würdest den ganzen Weg kämpfen, wenn du das tust. Zweitens sind Singletons nur statische Variablen mit einer gewissen Kapselung. Sie können also eine statische globale Variable verwenden. Globale Variablen sind jedoch typischerweise mit viel zu vielen Übeln verbunden. Sie könnten ansonsten eine lokale statische Funktionsvariable wie folgt verwenden:
int *SingletonInt() {
static int instance = 42;
return &instance;
}
oder ein intelligenteres Makro:
#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;
}
Und denken Sie schließlich daran, dass Singletons meistens missbraucht werden. Es ist schwierig, sie richtig hinzubekommen, besonders in Umgebungen mit mehreren Threads …
Danke, nur überprüft – Diese Lösung ist nicht an Multithread-Umgebungen angepasst, richtig?
– Liran Orevi
29. April 2009 um 19:00 Uhr
Diese Art der Initialisierung ist in einer Multithread-Umgebung sicher, wenn keine andere Abhängigkeit der Singleton-Instanz von anderen statischen Elementen im Programm vorhanden ist Dies aus ihrer C++-Implementierung.) Der Nachteil ist, dass Sie nur einen konstanten Ausdruck verwenden können. Ein Double-Checked-Locking-basiertes Singleton für eine Multithread-Umgebung benötigt eine Thread-Bibliothek für Mutexe usw.
– dirkgent
29. April 2009 um 19:14 Uhr
C ist mehr als geeignet für OOP. Tatsächlich sind die einzigen Funktionen von C++, die OOP wirklich helfen, syntaktischer Zucker.
– Javier
29. April 2009 um 19:19 Uhr
Beginnen wir mit Polymorphismus in C?
– dirkgent
29. April 2009 um 19:23 Uhr
Ich bestreite nicht, dass dies möglich ist, aber nicht ohne übermäßig viel Zeit und Mühe. Beispiel: Polymorphe Funktionen mit Makros zu hacken ist eine Möglichkeit – aber Sie verlieren die Typsicherheit. Es gibt auch kein RTTI in C. (Und ja, C hat einen begrenzten Polymorphismus, denken Sie an die Operatoren +, – usw.)
– dirkgent
29. April 2009 um 19:26 Uhr
Das müssen Sie nicht. C verfügt bereits über globale Variablen, sodass Sie keine Problemumgehung benötigen, um sie zu simulieren.
Ich verstehe, wie ein Global verwendet werden kann, aber die Tatsache, dass C++ auch globale Variablen hat und dass in C++ die Singleton-Implementierung normalerweise nicht global ist, lässt mich nach mehr streben.
– Liran Orevi
30. April 2009 um 17:57 Uhr
C++ ist eine objektorientierte Sprache; C ist nicht. Sicher, du kann mache OOP in C, aber es ist hässlich, hacky und mühsam, damit umzugehen. Ebenso du kann Schreiben Sie fast-C in C++, aber warum verwenden Sie dann C++? Verwenden Sie also wirklich nur eine globale. Es macht keinen Sinn zu versuchen zu verschleiern, was Sie tun, indem Sie es mit einer Reihe von funktionslokalen statischen Variablen und Präprozessor-Makros verschleiern.
– Adam Jaskiewicz
30. April 2009 um 18:34 Uhr
Kann nicht mehr zustimmen, Singletons werden oft von Programmierern verwendet, die sich irgendwie schämen, Globals zu verwenden.
– Laurent
1. Juni 2012 um 8:44 Uhr
Imho ist die Verwendung globaler Variablen obskurer als eine DB db = get_db_instance ()
– gpilotino
2. Juli 2012 um 19:47 Uhr
Beantwortet nicht die Frage von OP. C++ hat auch globale Variablen; würden Sie die gleiche Logik anwenden und globale Variablen über ein Singleton in C++ empfehlen? Bitte lesen Sie die ausführliche Dokumentation für die Gründe, einen Singleton über Instanzvariablen zu verwenden. Bedenken Sie, dass C++ Compiler wurden zuerst in ‘C’ geschrieben.
– Technikbegeistert
15. Februar 2018 um 19:33 Uhr
justinhj
Es ist so ziemlich dasselbe wie die C++-Version. Haben Sie einfach eine Funktion, die einen Instanzzeiger zurückgibt. Es kann eine statische Variable innerhalb der Funktion sein. Umschließen Sie den Funktionsrumpf je nach Plattform mit einem kritischen Abschnitt oder einem pthread-Mutex.
#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;
};
Beachten Sie, dass Sie auch eine Funktion benötigen, um den Singleton freizugeben. Vor allem, wenn es Systemressourcen greift, die beim Beenden des Prozesses nicht automatisch freigegeben werden.
BEARBEITEN: Meine Antwort geht davon aus, dass das Singleton, das Sie erstellen, etwas komplex ist und einen mehrstufigen Erstellungsprozess hat. Wenn es sich nur um statische Daten handelt, wählen Sie eine globale, wie andere vorgeschlagen haben.
Ein Singleton in C wird sehr seltsam sein. . . Ich habe noch nie ein Beispiel für “objektorientiertes C” gesehen, das besonders elegant aussah. Ziehen Sie nach Möglichkeit die Verwendung von C++ in Betracht. Mit C++ können Sie auswählen, welche Funktionen Sie verwenden möchten, und viele Leute verwenden es einfach als “besseres C”.
Unten sehen Sie ein ziemlich typisches Muster für eine einmalige Initialisierung ohne Sperre. Der InterlockCompareExchangePtr tauscht den neuen Wert atomar aus, wenn der vorherige null ist. Dies schützt, wenn mehrere Threads gleichzeitig versuchen, den Singleton zu erstellen, nur einer gewinnt. Die anderen löschen ihr neu erstelltes Objekt.
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());
Hier ist eine andere Perspektive: Jede Datei in einem C-Programm ist effektiv eine Singleton-Klasse, die zur Laufzeit automatisch instanziiert wird und nicht abgeleitet werden kann.
extern
in irgendeiner Header-Datei).Geben Sie allem ein richtiges Präfix und jetzt können Sie verwenden my_singleton_method()
anstelle my_singleton.method()
.
Wenn Ihr Singleton komplex ist, können Sie a schreiben generate_singleton()
-Methode, um sie vor der Verwendung zu initialisieren, aber dann müssen Sie sicherstellen, dass alle anderen öffentlichen Methoden prüfen, ob sie aufgerufen wurde, und einen Fehler ausgeben, wenn dies nicht der Fall ist.
Es scheint sicherer zu sein, Accessor-Funktionen zu verwenden als globale nicht statische Variablen.
– Technikbegeistert
15. Februar 2018 um 19:41 Uhr
@Technophile Es ist nicht sicherer, es ist flexibler. Wenn Sie Zugriff auf eine Variable gewähren, geben Sie Zugriff auf eine Variable, egal ob es sich um einen direkten Zugriff oder über die Accessor-Funktion handelt. Der Vorteil der Verwendung von Zugriffsfunktionen besteht darin, dass Sie über die einfache E/A hinaus zusätzliche Richtlinien implementieren können.
– Benutzer4209701
18. Februar 2018 um 12:10 Uhr
„Sicherer“ in Bezug auf den Zugriff zumindest über eine API, die mit Gültigkeitsprüfungen usw. erweitert werden kann. Meinen Sie das mit „zusätzlicher Richtlinie“?
– Technikbegeistert
26. Februar 2018 um 13:35 Uhr
Ja das meinte ich
– Benutzer4209701
1. April 2018 um 14:21 Uhr
Ich denke, diese Lösung könnte die einfachste und beste für die meisten Anwendungsfälle sein …
In diesem Beispiel erstelle ich eine globale Dispatch-Warteschlange mit einer einzelnen Instanz, was Sie beispielsweise auf jeden Fall tun würden, wenn Sie Dispatch-Quellereignisse von mehreren Objekten verfolgen würden. In diesem Fall könnte jedes Objekt, das die Warteschlange auf Ereignisse überwacht, benachrichtigt werden, wenn eine neue Aufgabe zur Warteschlange hinzugefügt wird. Sobald die globale Warteschlange eingerichtet ist (via queue_ref()
), es kann mit dem verwiesen werden queue
Variable in jeder Datei, in der die Header-Datei enthalten ist (Beispiele sind unten angegeben).
In einer meiner Implementierungen habe ich angerufen queue_ref()
in AppDelegate.m (main.c würde auch funktionieren). Dieser Weg, queue
wird initialisiert, bevor irgendein anderes aufrufendes Objekt versucht, darauf zuzugreifen. Bei den restlichen Objekten habe ich einfach angerufen queue
. Das Zurückgeben eines Werts aus einer Variablen ist viel schneller als das Aufrufen einer Funktion und das anschließende Überprüfen des Werts der Variablen vor der Rückgabe.
In 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 */
In 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;
}
Benutzen:
#include "GlobalQueue.h"
in jeder Objective-C- oder C-Implementierungsquelldatei.queue_ref()
um die Dispatch-Warteschlange zu verwenden. Einmal queue_ref()
aufgerufen wurde, kann die Warteschlange über die genutzt werden queue
Variable in allen QuelldateienBeispiele:
Aufruf von 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()**);
Anrufwarteschlange:
dispatch_queue_t serial_queue_with_queue_target = dispatch_queue_create_with_target("serial_queue_with_queue_target", DISPATCH_QUEUE_SERIAL, **queue**));]
Es scheint sicherer zu sein, Accessor-Funktionen zu verwenden als globale nicht statische Variablen.
– Technikbegeistert
15. Februar 2018 um 19:41 Uhr
@Technophile Es ist nicht sicherer, es ist flexibler. Wenn Sie Zugriff auf eine Variable gewähren, geben Sie Zugriff auf eine Variable, egal ob es sich um einen direkten Zugriff oder über die Accessor-Funktion handelt. Der Vorteil der Verwendung von Zugriffsfunktionen besteht darin, dass Sie über die einfache E/A hinaus zusätzliche Richtlinien implementieren können.
– Benutzer4209701
18. Februar 2018 um 12:10 Uhr
„Sicherer“ in Bezug auf den Zugriff zumindest über eine API, die mit Gültigkeitsprüfungen usw. erweitert werden kann. Meinen Sie das mit „zusätzlicher Richtlinie“?
– Technikbegeistert
26. Februar 2018 um 13:35 Uhr
Ja das meinte ich
– Benutzer4209701
1. April 2018 um 14:21 Uhr
Nr
Mach einfach
void * getSingleTon() {
static Class object = (Class *)malloc( sizeof( Class ) );
return &object;
}
was auch in einer gleichzeitigen Umgebung funktioniert.
Es sollte sein static Class * object = ...; return object;
.
– alk
7. September 2015 um 10:33 Uhr