Ist es in Ordnung, eine statische Variable zu verwenden, zu initialisieren / registrieren Variablen?

StackOverflow https://stackoverflow.com/questions/2337446

  •  22-09-2019
  •  | 
  •  

Frage

Sprache: C ++ Toolkit: Qt4

Die Toolkit Ich verwende hat eine statische Methode namens int QEvent::registerEventType() meine eigenen Ereignistypen zu registrieren. Wenn ich diese QEvent Unterklasse muss ich diesen Wert der Basisklasse liefern. QEvent::QEvent(int type).

Ist es in Ordnung, eine statische Variable zu verwenden, dies beginnt vor der Anwendung zu nennen? Beachten Sie Folgendes:

//This is all in my .cpp file

static int myEventType;  //This will contain my registered type

/*If I create a static instance of this class the constructor 
  gets called before the main() function starts.
*/
class DoRegisterMyEventType {  
public:
  DoRegisterMyEventType() {
    myEventType = QEvent::registerEventType();
  }
};

static DoRegisterMyEventType doRegisterMyEventType;

//Here is the constructor for MyEvent class which inherits QEvent.
MyEvent::MyEvent()
  : QEvent(myEventType)
{
}

Wie ‚böse‘ ist das? Ich konnte die ganze Sache in einem Namespace wickeln Verschmutzung der globalen Namensraum zu verhindern.

War es hilfreich?

Lösung

Statische Ebene Initialisierung ist eine große Compiler abhängige Grauzone, wie andere erwähnt haben. Allerdings Initialisierung Funktionsebene ist keine Grauzone und kann zu Ihrem Vorteil verwendet werden.

static inline int GetMyEventType()
{
    static int sEventType = QEvent::registerEventType();
    return sEventType;
}

MyEvent::MyEvent()
  : QEvent(GetMyEventType())
{
}

Diese Lösung hat die Eigenschaft, dass registerEventType garantiert wird aufgerufen werden, bevor Sie Ihren Ereignistyp müssen, auch wenn Sie MyEvent bei statischer Initialisierung konstruieren, was gut ist, aber es ist offen Sie bis zu faden Fragen der Sicherheit, wenn es möglich ist, für MyEvent aufgebaut auf mehreren Threads werden.

Hier ist ein Thread-sichere Version, basierend auf boost :: call_once:

#include "boost/thread/once.hpp"

static boost::once_flag sHaveRegistered = BOOST_ONCE_INIT; //This is initialized statically, effectively at compile time.    
static int sEventType = -1; //-1 is not a valid event

static void DoRegister()
{
    sEventType = QEvent::registerEventType();
}

static inline int GetMyEventType()
{
    boost::call_once(sHaveRegistered, &DoRegister);
    return sEventType;
}

Andere Tipps

Da C ++ 's Initialisierung über TUs ist eine große Grauzone mit viel Umsetzung Spielraum, ich Schrott bevorzugen es vollständig und über explizite werden, was, wenn gemacht wird. (Diese Ablehnung von Initialisierungsreihenfolge wegen des Mangels an Garantien ist ähnlich wie Singleton-Klassen globale Objekte abzulehnen.) Konkret bedeutet dies, jeden globaler Zustand (globale Variablen, statische Datenelemente und funktions lokale Statik), die nicht mit konstantem initialisiert werden kann Ausdrücke müssen in genau einer TU initialisiert werden, und dass TU ist die, dass Geräte Haupt .

Im manuellen Fall bedeutet dies, Einfügen und Code in der Übersetzungseinheit zu aktualisieren, die enthalten Haupt und in main selbst. Die häufigste Beispiel für einen solchen Code wird srand(time(0)) Aufruf der std :: rand PRNG.

Saatgut

können Sie Refactoring, dass manuelle Code-Management mit dem Prä-Prozessor:

// the implementation file for main, could be named main.cpp

#include "whatever_declares_the_real_main.hpp"

#include "global_objects.inc"

int main(int argc, char* argv[]) try {
#include "main_init.inc"

  return the_real_main(argc, argv);

  // main.cpp has well-defined responsibility:
  // initialize global state before passing control to another function, and
  // handle return-code or exceptions

  // you can modify this, depending on your preference and desired API
  // for example:
  return the_real_main(std::vector<std::string>(argv+1, argv+argc));
  return the_real_main(parse_args(argv+1, argv+argc));
  // just make sure to keep main.cpp's responsibility well-defined and
  // relatively simple
}
// example handling; depending on your specifics, you might do something
// different, or know how to provide more information:
catch (std::exception& e) {
  std::cerr << "abnormal termination: " << e.what() << '\n';
  return 1;
}
catch (...) {
  std::cerr << "abnormal termination.\n";
  return 1;
}

Diese .inc Dateien sind weder Header noch Implementierungsdateien. Die genaue Dateierweiterung spielt keine Rolle, solange man nicht etwas verwenden, die üblicherweise für Kopf- oder Implementierungsdateien, wie .h verwendet wird, .hpp, .cc, CPP, und so weiter. Sie können generieren global_objects.inc und main_init.inc basierend off Dateibenennungskonventionen, mit Wachen enthalten, so dass Abhängigkeiten enthalten sein können (ebenso umfassen Wachen Arbeit für Header).

Zum Beispiel entsprechen diese beiden Dateien mit myevent.hpp und würde neben diesem Header gesetzt werden:

// file "myevent.global_inc"
#ifndef INCLUDE_GUARD_37E6F5857F8F47918A7C83F29A9DA868
#define INCLUDE_GUARD_37E6F5857F8F47918A7C83F29A9DA868

#include <QEvent.hpp> // or whatever headers you need

#include "myevent.hpp" // declares the variable defined just below
// (remember you use 'extern' to declare objects without defining them)

int your_namespace::myEventType = QEvent::registerEventType();

#endif

// file "myevent.main_inc"
#ifndef INCLUDE_GUARD_4F1B93D0F4D3402B802CBA433241AA81
#define INCLUDE_GUARD_4F1B93D0F4D3402B802CBA433241AA81

// nothing needed in this case, from what you've shown so far

// this is where you place expressions that would otherwise require a dummy
// global variable to make sure they are executed, but this also allows use
// of temporary variables while includes handle dependency order:
#include "something_else.main_inc" // fake example dependency, which must
{                                  // be executed first
  int temp;
  some_func(&temp);
  other_func(temp); // not easy to transform this into a global's init
  // expression, yet defining it this way is natural, because it's exactly
  // how you would do it inside a function
}

#endif

Beachten Sie, dass, wenn Sie nur statische Daten Initialisierung mit konstanter Ausdrücke benötigen, dann, dass über alle anderen Techniken bevorzugt. Die primäre Beschränkung für die Initialisierung in der Lage zu keinem Funktionsaufruf zu machen (aber es ist tatsächlich komplizierter), so dass es nicht in Ihrem Fall nicht anwendbar; Das ist die einzige Art von globalen Variablen Initialisierung, dass C tun können, wenn Sie mehr erfahren möchten.

Ich verwende das „statische Register-Objekt“ Muster ziemlich viel, aber Sie müssen ein großes Problem bewusst sein - Sie müssen sicherstellen, dass das, was Sie mit registrieren, die sich wahrscheinlich statisch sein, bevor das Ding geschaffen Sie registrieren. Als C ++ nicht die Reihenfolge der statischen Konstruktion zwischen Übersetzungseinheiten gewährleisten, kann dies problematisch sein. Eine Lösung ist die so genannte Meyer Singleton zu verwenden:

class Registry {
  public:
    static Registry & Instance() {
        static Registry r;
        return r;
    }

    ... 

 private:
    Registry() {    
      ...
    }
};

Wie alle Verweise auf das Register durch die Instanz () -Methode gehen müssen, werden Sie die gewünschte Baubefehl garantiert.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top