Frage

Angenommen, ich habe eine freie Funktion namens namens InitFoo. Ich möchte, dass diese Funktion vor Zufall mehrmals aufgerufen wird. Ohne viel dachte, ich habe Folgendes geschrieben:

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

    //Actual code goes here.
}

Das sieht jedoch nach einer großen Warze aus. InitFoo tut nicht müssen andere staatliche Informationen bewahren. Kann jemand einen Weg vorschlagen, das gleiche Ziel ohne die Hässlichkeit zu erreichen?

Makros zählen natürlich nicht.

War es hilfreich?

Lösung

Sie können es mit etwas anderer Hässlichkeit tun:

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

void Foo()
{
    static InitFoo i;
}

Du benutzt immer noch static, aber jetzt müssen Sie nicht Ihre eigene Flaggenüberprüfung durchführen - static legt bereits eine Flagge und einen Scheck dazu ein, so dass es nur konstruiert i einmal.

Andere Tipps

Nun, ein Konstruktor wird nur einmal automatisch aufgerufen. Wenn Sie eine einzelne Instanz dieser Klasse erstellen:

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

Dann //do stuff wird nur einmal ausführen. Die einzige Möglichkeit, es zweimal auszuführen, besteht darin, eine andere Instanz der Klasse zu erstellen.

Sie können dies verhindern, indem Sie a verwenden Singleton. Tatsächlich, //do stuff kann nur einmal aufgerufen werden.

Ich möchte, dass diese Funktion vor Zufall mehrmals aufgerufen wird

Für mich klingt dies nach einem Problem, das nur beim Debuggen auftaucht. Wenn dies der Fall ist, würde ich einfach Folgendes tun:

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

    ...
}

Der Zweck dieser besonderen Warze ist leicht zu erkennen, indem er sie ansieht, und es wird einen schönen, großen, auffälligen Behauptungsversagen verursachen, wenn ein Programmierer jemals den Fehler macht, anzurufen InitFoo Mehr als einmal. Es wird auch im Produktionscode vollständig verbreitet. (Wenn NDEBUG ist definiert).

bearbeiten: Eine kurze Anmerkung zur Motivation:
Das Aufrufen einer Init -Funktion mehr als einmal ist wahrscheinlich ein großer Fehler. Wenn der Endbenutzer dieser Funktion es fälschlicherweise zweimal bezeichnet hat, ist es wahrscheinlich nicht der richtige Weg, diesen Fehler leise zu ignorieren. Wenn Sie nicht gehen assert() Route, ich würde empfehlen, zumindest eine Nachricht zu entsorgen stdout oder stderr.

Genau so würde ich es tun. Sie können einen Funktionszeiger verwenden, wenn Sie eine Alternative wünschen:

static void InitFoo_impl()
{
    // Do stuff.

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

void (*InitFoo)() = &InitFoo_impl;

Benötigen Sie es auch, um Multi-Thread-sicher zu sein? Schauen Sie sich das Singleton-Muster mit Double-Check-Verriegelung an (was leicht überraschend ist, sich falsch zu machen).

Wenn Sie keine ganze Klasse dafür wollen, ist ein weiterer einfacher Weg:

In a .cpp (nicht initblah im .h deklarieren)

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

Niemand kann es außerhalb dieses .cpp nennen und es wird aufgerufen. Sicher, jemand könnte es in diesem .CPP anrufen - hängt davon ab, wie sehr Sie sich interessieren, dass es unmöglich ist und unpraktisch und dokumentiert ist.

Wenn Sie sich um Reihenfolge kümmern oder dies zu einem bestimmten Zeitpunkt tun, ist Singleton wahrscheinlich für Sie.

Ich mache genau das die ganze Zeit mit Situationen, in denen einst die einmalige, aber nicht mehrwertige Making-a-Gloy-Klasse für die Klasse erforderlich ist. Natürlich wird davon ausgegangen, dass Sie sich keine Sorgen um Themen im Zusammenhang mit Thread machen. Normalerweise präfixe ich den Variablennamen mit "s_", um klar zu machen, dass es sich um eine statische Variable handelt.

Hmmm ... wenn Sie keine Einwände gegen die Verwendung haben Schub, schauen Sie sich dann an 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!
}

Nicht, dass ich sehr aufgeregt bin, aber dies ist nur ein anderer Weg: Funktionsobjekt.

#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();
}
Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top