2010-08-20 7 views
1

Initialisation J'ai lu récemment que Java arbore maintenant des blocs d'initialisation comme ce qui suit:propre syntaxe pour Changer Membre statique Comportement

class C { 

    public C() { /* Instance Construction */ } 
    static { /* Static Initialisation */ } 
    { /* Instance Initialisation */ } 

} 

Je suis particulièrement intéressé par le bloc static. Cela m'a fait penser au problème de l'ordre d'initialisation statique qui affecte beaucoup d'utilisateurs C++ novices, et aux solutions de contournement typiques, comme envelopper le membre statique dans une fonction gratuite ou en utilisant l'extension __attribute__((init_priority(n))) de GNU.

Je cherche un moyen d'écrire une méthode qui s'appellera automatiquement pour initialiser les membres statiques d'une classe, que ce soit lors de la création de la première instance ou simplement au début du programme, pendant les phases statiques ordinaires initialisation

Jusqu'à présent, c'est le meilleur qui est venu à l'esprit:

class C { 
private: 

    class Static { 
    public: 

     Static(); 

     int i; 
     Foo foo; 

    }; 

public: 

    static Static statics; 

    // ... 

}; 

C::Static::Static() : i(42), foo("bar") {} 

C'est, envelopper tous les membres statiques dans une classe et créer une instance statique de cette classe, dont le constructeur sert d'initialisation statique fonction. Il est simple de modifier ceci pour instancier le membre statics uniquement lorsqu'une instance est créée, et même pour assurer la destruction correcte des membres statiques.

Le problème avec ceci, bien sûr, est que C::foo devient C::statics.foo, ce qui n'est pas naturel. Y a-t-il un moyen de contourner la syntaxe maladroite ou existe-t-il une meilleure solution?

Répondre

3

Cela ne semble pas valoir la peine pour moi la plupart du temps. Vous n'améliorez vraiment pas la lisibilité, la construction inhabituelle va confondre les futurs programmeurs qui conservent votre code, et l'ajout de la nouvelle classe augmente la complexité, vous exposant ainsi au potentiel de plus de bugs.

Je peux voir comment vous pourriez rarement besoin ou vouloir contrôler l'ordre des initialisations statiques, ou pour toute autre raison, encapsuler toutes les statistiques. Mais d'un point de vue de la lisibilité, je préfère le essayé et vrai:

class C 
{ 
private: 
    static int i; 
    static Foo foo; 
}; 

int C::i = 42; 
Foo C::foo("bar"); 

Si vous avez beaucoup de ces statics, pourquoi ne pas envoyer tout de leur propre dossier du RPC? Mais là encore, si vous avez vraiment beaucoup de statique, je me demande s'il y a quelque chose qui ne va pas dans la conception globale ...

+0

d'accord, si vous avez beaucoup de statique dans vos classes peut-être l'approche est fausse en premier lieu. –

+0

+1 pour la dernière phrase sur la conception globale. –

+0

Considérez cela comme une curiosité. Comme vous le dites, dans certaines situations, cela pourrait être utile. Le fait est que C++ offre au programmeur très peu de contrôle sur la durée de vie des instances statiques, et un idiome propre pour étendre ce contrôle ne peut pas nuire. –

0

Pour être clair, il y a en fait trois problèmes avec les variables globales en général (pas seulement statics):

  • Initialisation Commander Fiasco
  • destruction Commander Fiasco
  • multi-threading initialisations multiples

Bien sûr, la plupart du temps s la destruction n'est pas tellement un problème, mais elle existe toujours. Le nouveau C++ 0x est sensible au thread, et en tant que tel, il y a du gain (en particulier pour les variables statiques locales concernant le problème du multithread). Avec l'apparition de C++ 0x, le code suivant peut seulement souffrir du "Destruction Order Fiasco" ... sauf si vous avez bien sûr une référence cyclique dans l'initialisation.

// foo.h 
class Foo 
{ 
public: 
    static Foo& Instance() { static Foo M; return M; } 

private: 
    Foo(); 
}; 

// bar.h 
// idem for Bar 

// foo.cpp 
Foo::Foo() { Bar& bar = Bar::Instance(); .... } 

Cela fonctionne très bien: puisque l'instance est créée à la demande, la langue garantit qu'il est là quand vous en avez besoin, et C++ 0x garantit le comportement même si plusieurs threads tentent d'accéder à la fonction à la fois.

Maintenant, qu'en est-il du "Destruction Order Fiasco"? Bien, les objets sont détruits dans l'ordre inverse de leur construction, donc si vous avez besoin d'accéder à un objet dans votre destructeur, accédez-y dans votre constructeur juste pour s'assurer qu'il est construit avant vous, et tout va bien.

Bien sûr, sans C++ 0x, les choses sont un peu plus lourdes. Pour éviter le problème multi-thread, le seul conseil est d'accéder à toutes les variables d'abord alors que l'application est toujours à un seul thread (en main). De cette façon, toutes les instances sont créées et il n'y a plus de problème de concurrence.