Вопрос

Предположим, у меня есть бесплатная функция под названием InitFoo. Анкет Я хотел бы защитить эту функцию от неожиданности несколько раз. Не думал, что написал следующее:

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

    //Actual code goes here.
}

Это выглядит как большая бородавка, хотя. InitFoo делает нет Нужно сохранить любую другую информацию о состоянии. Может ли кто -нибудь предложить способ достичь одной и той же цели без уродства?

Макросы не считаются, конечно.

Это было полезно?

Решение

Вы можете сделать это с каким -то другим уродством:

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

void Foo()
{
    static InitFoo i;
}

Вы все еще используете static, но теперь вам не нужно проверять собственную проверку флага - static уже ставит флаг и проверку на него, так что он только конструирует i однажды.

Другие советы

Ну, конструктор называется только один раз. Если вы создаете один экземпляр этого класса:

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

затем //do stuff будет выполнять только один раз. Единственный способ выполнить его дважды - это создать еще один экземпляр класса.

Вы можете предотвратить это, используя Синглтон. Анкет В результате, //do stuff может быть вызван только один раз.

Я хотел бы защитить эту функцию от случайного называться несколько раз случайно

Для меня это звучит как проблема, которая появится только во время отладки. Если это так, я бы просто сделал следующее:

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

    ...
}

Цель этой конкретной бородавки легко различена, просто посмотрев на нее, и это вызовет хороший, большой, яркий провал утверждения, если программист когда -либо совершит ошибку, звоня InitFoo больше чем единожды. Он также полностью исчезнет в производственном коде. (когда NDEBUG определено).

редактировать: Быстрая заметка о мотивации:
Вызов функции init более одного раза, вероятно, является большой ошибкой. Если конечный пользователь этой функции ошибочно называл ее дважды, тихо игнорировать эту ошибку, вероятно, не путь. Если вы не пойдете assert() маршрут, я бы порекомендовал, по крайней мере, выбросить сообщение stdout или же stderr.

Именно так я бы это сделал. Вы могли бы использовать какую -то перетасовку с указателем функции, если хотите альтернативы:

static void InitFoo_impl()
{
    // Do stuff.

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

void (*InitFoo)() = &InitFoo_impl;

Вам также нужно, чтобы он был в безопасности многопоточной? Посмотрите на шаблон Singleton с блокировкой двойной проверки (что удивительно легко ошибаться).

Если вы не хотите целого класса для этого, еще один простой способ:

В .cpp (не объявляйте initblah в .h)

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

Никто не может назвать это вне этого .cpp, и его вызывают. Конечно, кто -то мог бы назвать это в этом .cpp - зависит от того, насколько вы заботитесь о том, что это невозможно по сравнению с неудобными и задокументированными.

Если вы заботитесь о заказе или делаете это в определенное время, то Singleton, вероятно, для вас.

Я делаю именно это все время с ситуациями, которые нуждаются в одном из одноразовых, но не поставляя, а также класс. Конечно, предполагается, что вы не беспокоитесь о проблемах, связанных с нитью. Обычно я префикс имени переменной с «s_», чтобы ясно прояснил, что это статическая переменная.

Хммм ... если вы не возражаете против использования Способствовать росту, затем взглянуть на 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!
}

Не то чтобы я очень взволнован этим, но это просто еще один способ: функциональный объект.

#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();
}
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top