Pergunta

Suponha que eu tenha uma função gratuita chamada InitFoo. Eu gostaria de proteger essa função de ser chamado várias vezes por acidente. Sem pensar muito, escrevi o seguinte:

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

    //Actual code goes here.
}

Parece uma verruga grande, no entanto. InitFoo faz não precisa preservar qualquer outra informação do estado. Alguém pode sugerir uma maneira de alcançar o mesmo objetivo sem a feiúra?

As macros não contam, é claro.

Foi útil?

Solução

Você pode fazer isso com alguma feiura diferente:

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

void Foo()
{
    static InitFoo i;
}

Você ainda está usando static, mas agora você não precisa fazer sua própria verificação de bandeira - static já coloca uma bandeira e um cheque, então só constrói i uma vez.

Outras dicas

Bem, um construtor é chamado apenas automaticamente uma vez. Se você criar uma única instância desta classe:

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

Então //do stuff só será executado uma vez. A única maneira de executá -lo duas vezes é criar outra instância da classe.

Você pode evitar isso usando um Singleton. Na verdade, //do stuff só pode ser chamado uma vez.

Eu gostaria de proteger essa função de ser chamado várias vezes por acidente

Para mim, isso parece um problema que só surgirá durante a depuração. Se for esse o caso, eu simplesmente faria o seguinte:

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

    ...
}

O objetivo desta verruga em particular é facilmente discernido apenas olhando para ela, e causará uma falha agradável, grande e chamativa se um programador cometer o erro de ligar InitFoo mais de uma vez. Também vai desaparecer completamente no código de produção. (quando NDEBUG é definido).

editar: Uma nota rápida sobre a motivação:
Chamar uma função init mais de uma vez é provavelmente um grande erro. Se o usuário final dessa função o chamou por engano duas vezes, ignorar silenciosamente esse erro provavelmente não é o caminho a seguir. Se você não for o assert() rota, eu recomendaria pelo menos despejar uma mensagem para stdout ou stderr.

É exatamente assim que eu faria. Você pode usar algum ponteiro de função se arrastando se quiser uma alternativa:

static void InitFoo_impl()
{
    // Do stuff.

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

void (*InitFoo)() = &InitFoo_impl;

Você também precisa ser um seguro com vários thread? Olhe para o padrão de singleton com bloqueio de check duas vezes (o que é surpreendente fácil de errar).

Se você não quer uma aula inteira para isso, outra maneira simples é:

Em um .cpp (não declare initblah no .h)

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

Ninguém pode chamá -lo fora deste .CPP, e ele é chamado. Claro, alguém poderia chamá -lo neste .cpp - depende de quanto você se importa que seja impossível versus inconveniente e documentado.

Se você se preocupa com o pedido ou fazê -lo em um horário específico, Singleton provavelmente é para você.

Faço exatamente isso o tempo todo com situações que precisam daquela classificação única, mas não o valor, que não fazia uma classe. Obviamente, pressupõe que você não se preocupe com problemas relacionados a linhas. Normalmente, prefixo o nome da variável com "S_" para deixar claro que é uma variável estática.

Hmmm ... se você não se opõe a usar Impulso, então dê uma olhada em 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!
}

Não que eu esteja muito empolgado com isso, mas isso é apenas outra maneira: o objeto de função.

#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 em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top