2009-04-29 22 views

Respuesta

27

En primer lugar, C no es adecuado para la programación OO. Estarías peleando todo el camino si lo haces. En segundo lugar, los singletons son solo variables estáticas con alguna encapsulación. Entonces puedes usar una variable global estática. Sin embargo, las variables globales suelen tener demasiadas enfermedades asociadas a ellas. De lo contrario, podría utilizar una variable local de la función estática, así:

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

o una macro inteligente:

#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; 
} 

Y, por último, recuerde, que son en su mayoría hijos únicos abusado. Es difícil hacerlo bien, especialmente en entornos de subprocesos múltiples ...

+0

Gracias, solo comprobando - Esta solución no está adaptada a entornos de subprocesos múltiples, ¿verdad? –

+0

Esta forma de inicialización es segura en un entorno de subprocesos múltiples cuando no hay otra dependencia de la instancia singleton en ningún otro estático presente dentro del programa. (IIRC, hay una cierta ambigüedad en el orden de inicialización de statics, por lo que Gamma et al dejó esto fuera de su implementación en C++). La desventaja es que solo puedes usar una expresión constante. Un singleton con bloqueo doblemente verificado para entornos con múltiples subprocesos necesitará una biblioteca de subprocesos para mutexes, etc. – dirkgently

+2

C es más que adecuado para OOP. de hecho, las únicas características de C++ que realmente ayudan a OOP son el azúcar sintáctico. – Javier

4

EDITAR: Mi respuesta supone que el singleton que está creando es algo complejo y tiene un proceso de creación de varios pasos. Si solo se trata de datos estáticos, opte por un sistema global como otros han sugerido.

Un singleton en C será muy extraño. . . Nunca he visto un ejemplo de "C orientada a objetos" que se viera particularmente elegante. Si es posible, considere usar C++. C++ le permite escoger y elegir qué características desea usar, y muchas personas simplemente lo usan como una "C mejor".

A continuación se muestra un patrón bastante típico para la inicialización de una sola vez sin bloqueo. InterlockCompareExchangePtr intercambia atómicamente el nuevo valor si el anterior es nulo. Esto protege si varios hilos intentan crear el singleton al mismo tiempo, solo uno ganará. Los demás eliminarán su objeto recién creado.

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()); 
+0

+1 Bonito patrón: aunque esta técnica supone que puede llamar a CreateNewObj más de una vez. Dependiendo de los detalles de los recursos manejados por Singleton, es posible que no tenga ese lujo. – Eclipse

+0

Es verdad, si eso no es posible, debe usar una solución más pesada, como un candado. – Michael

+0

En un sistema con pthreads, se puede usar pthread_once() para ejecutar el código de inicialización solo una vez. –

17

No es necesario. C ya tiene variables globales, por lo que no necesita una solución alternativa para simularlas.

+0

Entiendo la forma en que se puede usar un global, pero el hecho de que C++ también tenga variables globales, y que en C++ la implementación singleton generalmente no sea global, me hace aspirar a más ... –

+3

C++ es un lenguaje orientado a objetos; C no es. Claro, usted * puede * hacer OOP en C, pero es feo, hacky, y un dolor con el que lidiar. De manera similar, * puedes * escribir casi-C en C++, pero ¿por qué estás usando C++? Entonces realmente, solo use un global. No tiene sentido tratar de disfrazar lo que está haciendo al ofuscarlo con un conjunto de variables estáticas locales de funciones y macros de preprocesador. –

+0

No podría estar más de acuerdo, los solteros suelen ser utilizados por programadores que de alguna manera están avergonzados de usar globales. –

12

Es prácticamente la misma versión de C++. Solo tiene una función que devuelve un puntero de instancia. Puede ser una variable estática dentro de la función. Envuelva el cuerpo de la función con una sección crítica o pthread mutex, según la plataforma.

#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; 
}; 

Tenga en cuenta que también necesita una función para liberar el singleton. Especialmente si captura cualquier recurso del sistema que no se libera automáticamente al salir del proceso.

-2

Eso sí,

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

que trabaja en un entorno concurrente también.

+0

Debe ser 'static Class * object = ...; objeto de retorno; '. – alk

2

Aquí hay otra perspectiva: cada archivo en un programa C es efectivamente una clase singleton que se instancia automáticamente en el tiempo de ejecución y no se puede subclasificar.

  • Las variables globales estáticas son sus miembros privados de la clase.
  • Global no estático son públicos (simplemente júntelos usando extern en algún archivo de encabezado).
  • Las funciones estáticas son métodos privados
  • Las funciones no estáticas son las públicas.

Dale a todo un prefijo adecuado y ahora puedes usar my_singleton_method() en lugar de my_singleton.method().

Si su singleton es complejo, puede escribir un método generate_singleton() para inicializarlo antes de usarlo, pero luego debe asegurarse de que todos los demás métodos públicos verifiquen si se invocó y si no el error.

+0

Parece más seguro usar funciones de acceso en lugar de variables no estáticas globales. – Technophile

+0

@Technophile No es más seguro, es más flexible. Si le da acceso a una variable, le está dando acceso a una variable, no importa si es de acceso directo o mediante la función de acceso. El beneficio de usar funciones de acceso es que puede implementar una política adicional más allá de la E/S directa. –

+0

'Más seguro' en términos de al menos acceso de canalización a través de una API, que puede ampliarse con verificaciones de validez, etc. ¿Es esto lo que quiere decir con "política adicional"? – Technophile