Question

Cette question liée à l'initialisation des objets en C ++

J'ai un groupe de classes (pas d'instances), héritant d'une classe de base commune, et je les ai besoin d'enregistrer les informations sur eux-mêmes dans un récipient (en particulier une carte) au démarrage du programme.

Le problème est que je besoin d'être dynamique. Le conteneur est défini dans un projet indépendant, différent des classes. Je préférerais éviter de faire plusieurs versions codées en dur de la bibliothèque, un pour chaque ensemble de classes dans chaque programme qui l'utilise.

Je pensais que d'avoir une instance statique d'une classe spéciale dans chacune de ces sous-classes, qui rendrait l'enregistrement dans son constructeur. Cependant, j'ai trouvé aucun moyen de garantir que le conteneur aura été construit avant la construction de ces objets.

Il faut aussi noter que les informations contenues dans le conteneur sur les sous-classes devrait être disponible avant toute instance de ces sous-classes est créé.

Est-il possible de le faire, ou imiter un constructeur statique en C ++ en général?

Était-ce utile?

La solution

Vous décrivez des problèmes différents à la fois. Sur la question particulière d'avoir une sorte de initialisation statique , une approche simple est de créer une classe de faux qui effectuera l'enregistrement. Ensuite, chacun des différentes classes pourrait avoir un membre de static const X, le membre devra être défini dans une unité de traduction, et la définition déclenchera l'instanciation de l'instance et l'enregistrement de la classe.

Cela ne traite pas le problème difficile, ce qui est le fiasco de l'ordre initailization. La langue ne fournit aucune garantie sur l'ordre d'initialisation des objets dans différentes unités de traduction. Autrement dit, si vous compilez trois unités de traduction avec ces classes, il n'y a aucune garantie sur l'ordre relatif de l'exécution de l'élément faux. Cela est également appliqué à la bibliothèque:. Il n'y a aucune garantie que le conteneur dans lequel vous souhaitez enregistrer vos cours a été initialisé, si un tel conteneur est un attribut membre global / statique

Si vous avez accès au code, vous pouvez modifier le code de conteneur à utiliser static local variables, et ce sera un pas en avant pour assurer l'ordre d'initialisation. Comme une esquisse d'une solution possible:

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

Dans ce cas, il n'y a pas d'ordre défini entre l'inscription des classes a et b, mais cela pourrait ne pas être un problème. D'autre part, en utilisant un variable statique locale dans la fonction registry::instance l'initialisation du singleton est garanti à effectuer avant tout appel aux méthodes de registry::register (dans le cadre de le premier appel à la méthode de instance).

Si vous ne pouvez pas faire ce changement, vous êtes essentiellement hors de la chance et vous ne pouvez pas garantir que le registry sera instancié avant que les autres attributs membres de statique (ou) dans d'autres GLOBALS unités de traduction. Si tel est le cas, alors vous devrez reporter l'enregistrement de la classe à la première instanciation et ajouter du code au constructeur de chaque classe à être enregistrée qui assure que la classe est enregistrée avant réelle construction de l'objet.

Cela pourrait ou non être une solution, selon que tout autre code crée des objets du type ou non. Dans le cas particulier des fonctions d'usine (premier qui est venu à l'esprit), si rien ne permet d'autre de créer des objets de types a ou b ... puis l'enregistrement de support piggy sur les appels du constructeur ne sera pas une solution non plus.

Autres conseils

Il est contre paradigme OOP, mais que diriez-vous d'avoir vos membres statiques forment une liste chaînée guidée par 2 variables globales? Vous pourriez faire quelque chose comme ça:

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

croire les variables globales, car ils ne ont pas besoin ai constructeur , mais ne sont que des données pures, sont garantis déjà initialisés lorsque vous commencez l'exécution de votre code.

Il est évident que cela est thread-safe, etc, mais devrait faire votre travail.

est un candidat pour le Singleton modèle . En gros, vous voulez que le conteneur à instancier lorsque la première instance d'une sous-classe est instancié. Cela peut être facilité en vérifiant si le pointeur NULL singleton est dans le constructeur de classe de base, et le cas échéant, instancier puis le récipient.

Une idée est de passer un Inscription foncteur aux classes. Chaque descendant exécuterait la fonction d'enregistrer. Ce foncteur pourrait être passé dans le constructeur.

Exemple:

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

Voir: http://www.parashift.com/ c ++ - faq-lite / ctors.html # faq-10,14

L'une des options consiste à construire le conteneur paresseusement, quand la première chose est ajouté à ce:

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

La seule façon de « imiter » un constructeur statique est d'appeler explicitement une fonction pour effectuer votre initialisation statique. Il n'y a pas d'autre moyen d'exécuter pré-principal code juste en liant dans un module.

Vous pouvez utiliser un « Initialiser la première utilisation » modèle, puis instancier une instance statique factice pour assurer l'initialisation le plus tôt 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 ;
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top