Domanda

Questa una domanda relativa alla inizializzazione di oggetti in C ++

Ho un gruppo di classi (non istanze), eredita da una classe base comune, e ho bisogno di loro di registrare informazioni su se stessi in un contenitore (in particolare una mappa), quando il programma si avvia.

Il problema è che ho bisogno di essere dinamico. Il contenitore è definito in un progetto indipendente, diversa dalle classi. Io preferirei evitare di fare più versioni hard-coded della biblioteca, uno per ogni insieme di classi in ogni programma che l'utilizza.

ho pensato di avere un'istanza statica di una classe speciale in ciascuna di queste sottoclassi, che renderebbe l'iscrizione nel suo costruttore. Tuttavia, ho trovato alcun modo per garantire che il contenitore sarà stato costruito prima della costruzione di questi oggetti.

Si noti anche che le informazioni nel contenitore per le sottoclassi dovrebbe essere disponibile prima della creazione di qualsiasi istanza di queste sottoclassi.

C'è un modo per fare questo, o imitare un costruttore statico in C ++, in generale?

È stato utile?

Soluzione

si sta descrivendo diversi problemi tutti in una volta. Sulla questione particolare di avere una sorta di statica di inizializzazione , un approccio semplice è la creazione di una classe finta che eseguirà la registrazione. Poi ciascuna delle diverse classi potrebbe avere un membro static const X, il membro dovrà essere definito in un'unità di traduzione, e la definizione attiverà l'istanza dell'istanza e la registrazione della classe.

Questa non affronta il problema difficile, che è il fiasco ordine initailization. Il linguaggio non fornisce alcuna garanzia dell'ordine di inizializzazione di oggetti in diverse unità di traduzione. Cioè, se si compila tre unità di traduzione con tali classi, non v'è alcuna garanzia sul relativo ordine di esecuzione degli Stati falso. Ciò si applica anche alla biblioteca:. Non v'è alcuna garanzia che il contenitore in cui si desidera registrare le vostre classi è stato inizializzato, se tale contenitore è un attributo membro globale / static

Se si ha accesso al codice, è possibile modificare il codice del contenitore per uso static local variables, e che sarà un passo in avanti per assicurare l'ordine di inizializzazione. Come uno schizzo di una possibile soluzione:

// registry lib
class registry { // basically a singleton
public:
   static registry& instance() { // ensures initialization in the first call
      static registry inst;
       return inst;
   }
// rest of the code
private:
   registry(); // disable other code from constructing elements of this type
};
// register.h
struct register {
   template <typename T>
   register( std::string name ) {
       registry::instance().register( name, T (*factory)() ); // or whatever you need to register
   }
};
// a.h
class a {
public:
   static a* factory();
private:
   static const register r;
};
// a.cpp
const register a::r( "class a", a::factory );
// b.h/b.cpp similar to a.h/a.cpp

Ora, in questo caso non v'è alcun ordine preciso fra la registrazione delle classi a e b, ma che potrebbe non essere un problema. D'altra parte, utilizzando una variabile statica locale nella funzione registry::instance è garantita l'inizializzazione del singleton da eseguire prima qualsiasi chiamata ai metodi registry::register (come parte la prima chiamata al metodo instance).

Se non è possibile fare questo cambiamento si sono fondamentalmente fuori di fortuna e non si può garantire che il registry sarà un'istanza prima che gli altri attributi membri statici (o globali) in altre unità di traduzione. Se questo è il caso, allora si dovrà rinviare la registrazione della classe per la prima esemplificazione, e aggiungere il codice al costruttore di ciascuna classe deve essere registrato che assicura che la classe è registrata prima reale costruzione dell'oggetto.

Questo potrebbe o non essere una soluzione, a seconda che altro codice crea gli oggetti del tipo o meno. Nel caso particolare delle funzioni di fabbrica (il primo che è venuto in mente), se non altro è permesso di creare oggetti di tipo a o b ... poi piggy registrazione supporto per le chiamate costruttore non sarà una soluzione sia.

Altri suggerimenti

It is against OOP paradigm, but how about having your static members form a linked list guided by 2 global variables? You could do something like that:

ClassRegistrator *head=NULL;
ClassRegistrator *tail=NULL;

struct ClassRegistrator {
    ... //data that you need
    ClassRegistrator *next;
    ClassRegistrator(classData ...) {
      if (head==NULL) head=tail=this;
      else {
        tail->next=this;
        tail=this;
      }
      ... //do other stuff that you need for registration
    }
};


class MyClass { //the class you want to register
    static ClassRegistrator registrator;
}

ClassRegistrator MyClass::registrator(...); //call the constructor

I believe the global variables, as they don't need have a constructor, but are just pure data, are guaranteed to be already initialised when you begin the execution of your code.

Obviously this is not thread-safe, etc, but should make your job done.

This is a candidate for the Singleton pattern. Basically, you want the container to be instantiated when the first instance of a subclass is instantiated. This can be facilitated by checking if the singleton pointer is NULL in the base-class constructor, and if so, then instantiate the container.

One idea is to pass a registration functor to the classes. Each descendant would execute the function to register. This functor could be passed in the constructor.

Example:

struct Registration_Interface
{
  virtual void operator() (const std::string& component_name) = 0;
};

struct Base
{
};

struct Child1
  : public Base
{
  Child(Registration_Interface& registration_ftor)
  {
     //...
     registration_ftor("Child1");
  }
};

See: http://www.parashift.com/c++-faq-lite/ctors.html#faq-10.14

One option is to construct the container lazily, when the first thing is added to it:

  void AddToContainer(...) {
    // Will be initialized the first time this function is called.
    static Container* c = new Container();
    c->Add(...);
  }

The only way to "imitate" a static constructor is to explicitly call a function to perform your static initialization. There is no other way to run code pre-main just by linking in a module.

You might use an "initialise on first use" pattern, and then instantiate a dummy static instance to ensure initialisation as early as possible.

class cExample
{
    public :
       cExample() ;

       // Static functions here

    private :
        static bool static_init ;

        // other static members here
}

cExample::static init = false ;

cExample::cExample()
{
    // Static initialisation on first use
    if( !static_init )
    {
        // initialise static members
    }

    // Instance initialisation here (if needed)
}

// Dummy instance to force initialisation before main() (if necessary)
static cExample force_init ;
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top