E 'ok per utilizzare una variabile statica per inizializzare / registrare variabili?

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

  •  22-09-2019
  •  | 
  •  

Domanda

Lingua: C ++ Toolkit: Qt4

Il toolkit sto usando ha un metodo statico chiamato int QEvent::registerEventType() per registrare i miei tipi di evento. Quando ho sottoclasse questo QEvent ho bisogno di fornire la classe di base di questo valore. QEvent::QEvent(int type).

E 'ok per utilizzare una variabile statica per chiamare questo prima avvia l'applicazione? Si consideri il seguente:

//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)
{
}

Come 'male' è questo? Potrei avvolgere il tutto in uno spazio dei nomi per evitare che inquinare il namespace globale.

È stato utile?

Soluzione

inizializzazione livello statico è una vasta zona grigia compilatore-dipendente, come altri hanno detto. Tuttavia, l'inizializzazione livello di funzione non è una zona grigia e può essere utilizzato a proprio vantaggio.

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

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

Questa soluzione ha la proprietà che registerEventType è garantito per essere chiamato prima è necessario digitare il proprio evento, anche se si costruisce MyEvent durante l'inizializzazione statica, che è buono, ma non si apre fino a thread-safety problemi se è possibile per MyEvent essere costruito su più thread.

Ecco una versione thread-safe, sulla base di 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;
}

Altri suggerimenti

Dal momento che l'inizializzazione C ++ s ' tutta UT è una grande area grigia con molto margine di manovra implementazione, preferisco rottami completamente e essere esplicito su ciò che viene fatto quando. (Questo rifiuto di ordine di inizializzazione a causa della mancanza di garanzie è simile a come classi singleton rifiutano oggetti globali variabili inizializzare-register-.) In particolare, si intende qualunque stato globale (variabili globali, membri dati statici e statica funzione locale) che non possono essere inizializzate con del resistant espressioni devono essere inizializzati in esattamente un TU, e che la TU è quella che implementa principale .

Nel caso manuale, questo significa inserire e aggiornare il codice nell'unità di traduzione che contiene principale e principale stessa. L'esempio più comune di tale codice sta chiamando srand(time(0)) per seminare il std :: rand PRNG.

È possibile refactoring che la gestione del codice manuale utilizzando il preprocessore:

// 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;
}

Questi file .inc non sono né intestazioni né file di implementazione. L'estensione del file esatto non importa fintanto che non si utilizza qualcosa che è comunemente usato per le intestazioni o file di implementazione, come .h, .hpp, .cc, cpp, e così via. È possibile generare global_objects.inc e main_init.inc in base al largo le convenzioni di denominazione dei file, utilizzando includere guardie in modo che le dipendenze possono essere inclusi (proprio come includere guardie di lavoro per le intestazioni).

Per esempio, entrambi questi file corrispondono con myevent.hpp e sarebbe posizionato accanto a quella intestazione:

// 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

Si noti che se si richiedono solo l'inizializzazione di dati statici con costanti-espressioni, allora è preferibile rispetto tutte le altre tecniche. La limitazione principale per che l'inizializzazione non è essere in grado di effettuare una chiamata di funzione (ma in realtà è più complesso), in modo che non si applica nel tuo caso; questo è l'unico tipo di inizializzazione variabile globale che C può fare, se si vuole saperne di più.

Io uso il modello "oggetto registro statico" un bel po ', ma si deve essere consapevoli di un grosso problema - è necessario assicurarsi che la cosa si sta registrando con, che a sua volta è probabile che sia statica, viene creato prima la cosa si sta registrando. Come C ++ non garantisce l'ordine di costruzione statica tra unità di traduzione, questo può essere problematico. Una soluzione è quella di utilizzare il cosiddetto Meyer Singleton:

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

    ... 

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

Come tutti i riferimenti al Registro deve passare attraverso il metodo grado (), si sono garantiti l'ordine di costruzione necessari.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top