Pregunta

Supongamos que tengo una función gratuita llamada InitFoo. Me gustaría proteger esta función de ser llamado varias veces por accidente. Sin pensarlo, escribí lo siguiente:

void InitFoo()
{
    {
        static bool flag = false;
        if(flag) return;
        flag = true;
    }

    //Actual code goes here.
}

Sin embargo, esto parece una gran verruga. InitFoo lo hace no Necesita preservar cualquier otra información de estado. ¿Alguien puede sugerir una forma de lograr el mismo objetivo sin la fealdad?

Las macros no cuentan, por supuesto.

¿Fue útil?

Solución

Puedes hacerlo con un poco de fealdad diferente:

struct InitFoo
{
     InitFoo()
     {
         // one-time code goes here
     }
};

void Foo()
{
    static InitFoo i;
}

Sigues usando static, pero ahora no necesitas hacer tu propia verificación de bandera - static ya se pone en una bandera y una comprobación, por lo que solo se construye i una vez.

Otros consejos

Bueno, un constructor solo se llama automáticamente una vez. Si crea una sola instancia de esta clase:

class Foo
{
public:
    Foo(void)
    {
        // do stuff
    }
}

Después //do stuff solo se ejecutará una vez. La única forma de ejecutarlo dos veces es crear otra instancia de la clase.

Puede evitar esto usando un único. En efecto, //do stuff Posiblemente solo se puede llamar una vez.

Me gustaría proteger esta función de ser llamado varias veces por accidente.

Para mí, esto suena como un problema que solo surgirá durante la depuración. Si ese es el caso, simplemente haría lo siguiente:

void InitFoo()
{
    #ifndef NDEBUG
       static bool onlyCalledOnce = TRUE;
       assert(onlyCalledOnce);
       onlyCalledOnce = FALSE;
    #endif

    ...
}

El propósito de esta verruga en particular se discerne fácilmente simplemente mirándola, y causará una falla de afirmación agradable, grande y llamativa si un programador comete el error de llamar InitFoo mas de una vez. También desaparecerá por completo en el código de producción. (cuando NDEBUG se define).

editar: Una nota rápida sobre la motivación:
Llamar a una función init más de una vez es probablemente un gran error. Si el usuario final de esta función lo ha llamado por error dos veces, ignorar en silencio ese error probablemente no sea el camino a seguir. Si no vas al assert() ruta, recomendaría al menos descargar un mensaje a stdout o stderr.

Así es exactamente como lo haría. Podrías usar algún puntero de funciones barajando si quieres una alternativa:

static void InitFoo_impl()
{
    // Do stuff.

    // Next time InitFoo is called, call abort() instead.
    InitFoo = &abort;
}

void (*InitFoo)() = &InitFoo_impl;

¿También necesita que sea seguro de múltiples hilos? Mire en el patrón Singleton con bloqueo de doble verificación (lo cual es sorprendente fácil de equivocarse).

Si no quieres una clase completa para esto, otra forma simple es:

En un .cpp (no declares initblah en el .h)

 // don't call this -- called by blahInited initialization
static bool InitBlah() 
{
   // init stuff here
   return true;
}
bool blahInited = InitBlah();

Nadie puede llamarlo fuera de este .cpp, y se llama. Claro, alguien podría llamarlo en este .cpp: depende de cuánto le importe que sea imposible frente a inconveniente y documentado.

Si le importa el pedido o lo hace en un momento específico, entonces Singleton es probablemente para usted.

Hago exactamente eso todo el tiempo con situaciones que necesitan esa clase única pero no patrimonial-a-a-whole-class-para. Por supuesto, supone que no se preocupe por los problemas relacionados con los hilos. Por lo general, prefijo el nombre de la variable con "S_" para dejar en claro que es una variable estática.

Hmmm ... si no te opones a usar Impulsar, luego eche un vistazo a Boost :: Call_once:

namespace { boost::once_flag foo_init_flag = BOOST_ONCE_INIT; }

void InitFoo() {
    // do stuff here
}

void FooCaller() {
    boost::call_once(&foo_init_flag, InitFoo);
    // InitFoo has been called exactly once!
}

void AnotherFooCaller() {
    boost::call_once(&foo_init_flag, InitFoo);
    // InitFoo has been called exactly once!
}

No es que esté muy entusiasmado con eso, pero esta es solo otra forma: Function Object.

#import <iostream>

class CallOnce {
private:
    bool called;
public:
    CallOnce() {
        called = false;
    }
    void operator()(void) {
        if (called) {
            std::cout << "too many times, pal" <<std::endl;
            return;
        }
        std::cout << "I was called!" << std::endl;
        called = true;
    }

};

int main(void) {
    CallOnce call;

    call();
    call();
}
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top