Frage

Dies ist eine Frage in Bezug auf die Initialisierung von Objekten in C ++

Ich habe eine Gruppe von Klassen (nicht Instanzen), die von einer gemeinsamen Basisklasse erbt, und ich muss sie beim Start des Programms in einem Container (insbesondere einer Karte) Informationen über sich selbst registrieren.

Das Problem ist, dass ich es dynamisch sein muss. Der Container ist in einem unabhängigen Projekt definiert, das sich von den Klassen unterscheidet. Ich würde es vorziehen, mehrere hartcodierte Versionen der Bibliothek zu erstellen, eine für jeden Satz von Klassen in jedem Programm, das sie verwendet.

Ich dachte darüber nach, in jeder dieser Unterklassen eine statische Instanz einer speziellen Klasse zu haben, die die Registrierung in seinem Konstruktor vornehmen würde. Ich habe jedoch keine Möglichkeit gefunden, um zu garantieren, dass der Container vor dem Bau dieser Objekte gebaut wurde.

Ich sollte auch beachten, dass die Informationen im Container über die Unterklassen verfügbar sein sollten, bevor eine Instanz dieser Unterklassen erstellt wird.

Gibt es eine Möglichkeit, dies zu tun oder einen statischen Konstruktor in C ++ im Allgemeinen nachzuahmen?

War es hilfreich?

Lösung

Sie beschreiben verschiedene Probleme auf einmal. In der besonderen Frage der Art von Art von statische Initialisierung, Ein einfacher Ansatz besteht darin, eine gefälschte Klasse zu erstellen, die die Registrierung durchführt. Dann könnte jeder der verschiedenen Klassen a haben static const X Mitglied, das Mitglied muss in einer Übersetzungseinheit definiert werden, und die Definition löst die Instanz der Instanz und die Registrierung der Klasse aus.

Dies geht nicht mit dem harten Problem an, nämlich das Fiasko der Initailization Order. Die Sprache bietet keine Garantie für die Reihenfolge der Initialisierung von Objekten in verschiedenen Übersetzungseinheiten. Wenn Sie drei Übersetzungseinheiten mit solchen Klassen zusammenstellen, gibt es keine Garantie für die relative Ausführung des gefälschten Mitglieds. Dies gilt auch für die Bibliothek: Es gibt keine Garantie dafür, dass der Container, in dem Sie Ihre Klassen registrieren möchten, initialisiert wurde, wenn ein solcher Container ein globales/statisches Mitgliedsattribut ist.

Wenn Sie Zugriff auf den Code haben, können Sie den zu verwendenden Containercode ändern static local variables, und das wird ein Schritt nach vorne sein, um die Reihenfolge der Initialisierung zu gewährleisten. Als Skizze einer möglichen Lösung:

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

In diesem Fall gibt es unter der Registrierung des a und b Klassen, aber das ist vielleicht kein Problem. Andererseits mit a Lokale statische Variable in dem registry::instance Funktion Die Initialisierung des Singletons wird garantiert durchgeführt Vor Jeder Anruf an die registry::register Methoden (als Teil des ersten Anrufs an die instance Methode).

Wenn Sie diese Änderung nicht vornehmen können, haben Sie im Grunde genommen kein Glück und Sie können nicht garantieren, dass die registry wird vor den anderen statischen Mitgliedsattributen (oder Globalen) in anderen Übersetzungseinheiten instanziiert. Wenn dies der Fall ist, müssen Sie die Registrierung der Klasse in die erste Instanziation verschieben und dem Konstruktor jeder Klasse Code hinzufügen, um zu registrieren, die sicherstellen, dass die Klasse registriert ist Vor tatsächliche Konstruktion des Objekts.

Dies kann eine Lösung sein oder nicht, je nachdem, ob ein anderer Code Objekte des Typs erstellt oder nicht. Im Einzelfall von Fabrikfunktionen (zuerst, der mir in den Sinn kam), wenn nichts anderes Objekte von Typen erstellen darf a oder b... dann ist auch die Registrierung von Piggy -Unterstützung bei Konstruktoranrufen keine Lösung.

Andere Tipps

Es ist gegen OOP -Paradigma, aber wie wäre es, wenn Ihre statischen Mitglieder eine verknüpfte Liste bilden, die von 2 globalen Variablen geleitet wird? Sie könnten so etwas tun:

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

ich glauben die globalen Variablen, da sie keine benötigen Konstrukteur, aber nur reine Daten sind garantiert bereits initialisiert, wenn Sie mit der Ausführung Ihres Codes beginnen.

Offensichtlich ist dies kein Thread-Safe usw., sondern sollte Ihre Arbeit erledigen.

Dies ist ein Kandidat für die Singleton -Muster. Grundsätzlich möchten Sie, dass der Container instanziiert wird, wenn die erste Instanz einer Unterklasse instanziiert wird. Dies kann durch Überprüfung erleichtert werden, ob der Singleton-Zeiger im Basiskonstruktor null ist und wenn ja, dann den Container instanziiert.

Eine Idee ist es, a zu bestehen Anmeldung Functor zu den Klassen. Jeder Nachkomme würde die Funktion zur Registrierung ausführen. Dieser Funkor könnte im Konstruktor übergeben werden.

Beispiel:

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");
  }
};

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

Eine Möglichkeit besteht darin, den Container träge zu konstruieren, wenn das erste, was ihm hinzugefügt wird:

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

Die einzige Möglichkeit, einen statischen Konstruktor zu "imitieren", besteht darin, explizit eine Funktion aufzurufen, um Ihre statische Initialisierung auszuführen. Es gibt keinen anderen Weg, um Code vor dem Main auszuführen, indem Sie nur in einem Modul verknüpfen.

Sie können ein "initialise on zuerst verwenden" -Mustern verwenden und dann eine statische Dummy -Instanz instanziieren, um die Initialisierung so früh wie möglich zu gewährleisten.

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