IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

Langage C++ Discussion :

Ordre d'initialisation OO


Sujet :

Langage C++

Vue hybride

Message précédent Message précédent   Message suivant Message suivant
  1. #1
    Membre chevronné
    Inscrit en
    Août 2004
    Messages
    556
    Détails du profil
    Informations forums :
    Inscription : Août 2004
    Messages : 556
    Par défaut Ordre d'initialisation OO
    Salut !

    Je me pose une question, dont la réponse me semblait évident mais faute de trouver de source m'inquiète.

    Prenons le code suivant:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
     
    class A
    {
       public:
          A() {}
    };
     
    class B
    {
       public:
          B( A& a ) :
             _A( a ) {}
          A& _A;
    };
     
    class AB
    {
       public:
          AB() :
             _B( _A ), // ici l'ordre est inversé, c'est fait exprès
             _A()
          {
          }
     
       A _A;
       B _B;
    };
    Est-ce que ce code est sûr ?
    Est-ce qu'on peut affirmer que _A sera initialisé avant _B et que donc _B._A ne référencera pas une zone mémoire non alloué, ou est-ce que le comportement est indéfini par la norme ?

  2. #2
    Expert éminent
    Avatar de koala01
    Homme Profil pro
    aucun
    Inscrit en
    Octobre 2004
    Messages
    11 644
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 53
    Localisation : Belgique

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 644
    Par défaut
    Salut,

    Ce dont je suis sur, avant de faire la recherche dans la norme, c'est:
    1. les membres d'une classe sont initialisés dans l'ordre de leur déclaration dans le code
    2. avec un niveau d'avertissement suffisant, le compilateur devrait te sortir un avertissement du genre de Attention A est initialisé avant

    Et, pour toute sécurité, voici ce qu'elle dit précisément:
    Citation Envoyé par la norme 12.6.2 §5
    Initialization shall proceed in the following order:
    • First, and only for the constructor of the most derived class as described below, virtual base classes shall be initialized in the order they appear on a depth-first left-to-right traversal of the directed acyclic graph of base classes, where “left-to-right” is the order of appearance of the base class names in the derived class base-specifier-list.
    • Then, direct base classes shall be initialized in declaration order as they appear in the base-specifier-list (regardless of the order of the mem-initializers).
    • Then, nonstatic data members shall be initialized in the order they were declared in the class definition (again regardless of the order of the mem-initializers).
    • Finally, the body of the constructor is executed.
    Autrement dit, l'ordre dans lequel tu place les membres dans ta liste d'initialisation ne devrait pas intervenir (sauf restriction éventuelle du compilateur)
    A méditer: La solution la plus simple est toujours la moins compliquée
    Ce qui se conçoit bien s'énonce clairement, et les mots pour le dire vous viennent aisément. Nicolas Boileau
    Compiler Gcc sous windows avec MinGW
    Coder efficacement en C++ : dans les bacs le 17 février 2014
    mon tout nouveau blog

  3. #3
    Membre Expert

    Profil pro
    Inscrit en
    Juin 2006
    Messages
    1 294
    Détails du profil
    Informations personnelles :
    Localisation : Royaume-Uni

    Informations forums :
    Inscription : Juin 2006
    Messages : 1 294
    Par défaut
    Salut,

    Accessoirement donner à quelque chose un nom qui commence par un underscore produit un comportement indéfini, cf. Quels sont les identificateurs interdits par la norme ?.

    MAT.

  4. #4
    Rédacteur
    Avatar de 3DArchi
    Profil pro
    Inscrit en
    Juin 2008
    Messages
    7 634
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juin 2008
    Messages : 7 634
    Par défaut
    Citation Envoyé par koala01 Voir le message
    Autrement dit, l'ordre dans lequel tu place les membres dans ta liste d'initialisation ne devrait pas intervenir
    Je me permet de reformuler autrement : quelque soit l'ordre de ta liste d'initialisation, les membres sont toujours construits dans le même ordre que leur déclaration dans la classe.
    Ce qui a comme conséquence dans ton exemple que A est construit avant B quelque soit la forme que tu donnes à ta liste d'initialisation.

    En découlent des conseils de bonnes pratiques :
    1/ Il est fortement recommandé de suivre dans les listes d'initialisations l'ordre effectif de construction pour ne pas laisser croire à un relecteur du code qu'il pourrait en être autrement.
    2/ Il faut autant que possible éviter ce genre de dépendance (problème de conception ?).
    3/ Toute dépendance de construction entre les variables membres devra être explicitement commentée à défaut de pouvoir être évitée. Par cette documentation, les lecteurs du code sont avertis qu'il s'agit d'un comportement compris et maîtrisé par le développeur : la fiabilitié est accrue et la maintenance est facilitée.

  5. #5
    Membre chevronné
    Inscrit en
    Août 2004
    Messages
    556
    Détails du profil
    Informations forums :
    Inscription : Août 2004
    Messages : 556
    Par défaut
    Ok, merci pour ces réponses, en fait je demandais juste si le comportement était défini et certain, ou bien si c'était selon le bon vouloir du compilateur.

    Concernant ta dernière remarque, 3DArchi, en fait je pense que j'ai pas le choix.

    Actuellement j'ai, pour le network, plusieurs couches de classes qui ont des dépendances entre eux assez mal fichus, mais je sais pas comment réorganiser...

    classe Socket qui est en réalité une classe thread qui reçoit des données sur un port
    classe PacketProcessor qui se charge de lire et décrypter les données (il les reçoit de Socket) brut, les envois à IncomingPacketManager qui se charge de lire le détails du packet. Possède aussi une méthode "SendPacket" qui permet d'envoyer un packet à un Endpoint, en ayant donc construit son header et crypté.
    classe IncomingPacketManager qui se charge de lire le packet et de le dispatcher à une classe qui en sait plus sur le monde le message correspondant.
    classe MessageManager qui s'occupe des messages et des conséquences pour le monde
    classe OutgiongMessageManager qui reçoit données brut, en créer des packets, qui les envoi ensuite au PacketProcessor pour qu'il puisse les construire et les crypter.

    J'ai donc, un truc comme ça:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
     
    /*
     *  Socket
     *    ^
     *    |               
     *    v                           
     * PacketProcessor <---------------
     *    ^                           |
     *    |                           |
     *    v                           |
     * IncomingPacketManager          |
     *    |                           |
     *    v                           |
     * MessageManager                 |
     *    |                           |
     *    v                           |
     * OutgoingPacketManager  ---------
     */
    Et c'est vraiment la cata à organiser les dépendances. Ca a donné un mélange d'aggrégation et de références un peu partout, mais je vois pas comment faire autrement.

    C'est en voulant suivre l'idée de "une classe, un rôle" que j'en suis arrivé là :\

  6. #6
    Rédacteur
    Avatar de 3DArchi
    Profil pro
    Inscrit en
    Juin 2008
    Messages
    7 634
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juin 2008
    Messages : 7 634
    Par défaut
    C'est encore un peu abstrait pour que je comprenne où tu as ta dépendance de membre.
    Rassures-moi, c'est un graphe de flux de message et pas d'héritage ?

    P.S. : pour Socket/PacketProcessor, as-tu jeté un coup d'oeil à Présentation des principaux design patterns en C++ (décourateur) de David Come ?

  7. #7
    Membre chevronné
    Inscrit en
    Août 2004
    Messages
    556
    Détails du profil
    Informations forums :
    Inscription : Août 2004
    Messages : 556
    Par défaut
    C'est bien un graph de flux de message et non d'héritage :p (il n'y en a pas du tout en fait, dans ce cas-ci)

    Je ne vois pas en quoi le pattern decorateur m'avançerait en fait. Je vois pas trop ce que je peux en faire dans mon cas.

    Le code entier se trouve sur un svn sur ma machine et certaines parties sont quelque peu délicates, je vais donc m'abstenir de le mettre ne public sur le forum, mais je vais t'envoyer un PM si jamais tu veux y jeter un oeil et m'aiguiller un peu .

  8. #8
    Expert éminent
    Avatar de koala01
    Homme Profil pro
    aucun
    Inscrit en
    Octobre 2004
    Messages
    11 644
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 53
    Localisation : Belgique

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 644
    Par défaut
    A mon sens:
    • il est typiquement faux de dire qu'un socket est un thread...
    • PacketProcessor, IncomingPacketManager et OutgoingPacketManager en font beaucoup trop
    • MessageManager pourrait être un singleton

    Pour être plus clair:

    On ne peut pas décemment dire qu'un socket est un thread.

    Au pire, un socket a une composante thread, mais il s'agit d'une composition et non d'un héritage. (c'est peut être le cas, mais ta phrase laisse supposer le contraire)

    PacketProcessor serait idéalement scindé en deux parties, qui récupéreraient respectivement une responsabilité de IncomingPacketManager et de OutgoingPacketManager:
    • IncomingPacketProcessor : Recoit le message du socket, supprime l'en-tête du packet (après l'avoir vérifiée) et transmet le tout à IncomingPacketManager.

      Si l'en-tête est corrompue, il s'adresse à OutgoingPacketManager pour qu'il envoie une requête afin de récupérer le packet.
    • OutgoingPacketProcessor: Reçoit le message (sans en-tête) de OutgoingPacketManager, ajoute l'en-tête, et transmet le tout au socket pour émission

    IncomingPacketManager traite chaque packet, et les transmet vers PacketManager.

    Si le packet est inconsistant, il s'adresse à OutgoingPacketManager pour qu'il envoie une requete afin de le récupérer

    Packet manager reçoit les paquets traités de IncomingPacketManager pour les transmettre au monde et reçoit les information du monde pour les transmettre à OutgoingPacketManager

    OutgoingPacketManager crée le paquet (sans l'en-tête) à envoyer sur base des informations reçues et le transmet à OutgoingPacketProcessor.

    Au final, tu en arriverais à un diagramme de communication proche de mon petit schéma
    Images attachées Images attachées  
    A méditer: La solution la plus simple est toujours la moins compliquée
    Ce qui se conçoit bien s'énonce clairement, et les mots pour le dire vous viennent aisément. Nicolas Boileau
    Compiler Gcc sous windows avec MinGW
    Coder efficacement en C++ : dans les bacs le 17 février 2014
    mon tout nouveau blog

  9. #9
    Membre chevronné
    Inscrit en
    Août 2004
    Messages
    556
    Détails du profil
    Informations forums :
    Inscription : Août 2004
    Messages : 556
    Par défaut
    Merci Koala pour cette réponse, or il se trouve qu'à part le fait de scinder PacketProcessor en IncomingPP/OutgoingPP, tu as récité mot pour mot mon architecture actuelle .

    Effectivement, ce n'est qu'une méthode de Socket qui est un thread.
    Incoming/OutgoingPacketManager ne font en réalité rien de plus que ce que tu as décrit, sauf qu'au lieu de récupérer via/d'envers vers 2 objets différents, ils passent tous les 2 par PacketProcessor. (en réalité, c'est le PacketProcessor qui a besoin de IncomingPacketManager, et OutgoingPacketManager qui a besoin de PacketProcessor).

    Si je scinde PacketProcessor, OutgoingPacketManager aura toujours besoin de OutgoingPacketProcessor, ça ne m'avance pas beaucoup dans le sens où IncomingPacketManager ne connait déjà de toute manière pas directement PacketProcessor (c'est l'inverse).

    Le système en soi ne me déplait pas, loin de là, j'en suis même plutôt satisfait ; C'est l'initialisation ou "comment lancer tout ça ensemble" que j'ai du mal à déterminer proprement.

    Pour le médiateur, si je comprend bien, ça sous-entend une classe qui initialiserait chaque composant de manière non-dépendante à qui que ce soit si ce n'est le médiateur lui-même, et qui s'occuperait de dispatcher tous les events au bon endroit, c'est ça ? Ca me plait assez bien, ça a l'air séduisant comme approche mais, le médiateur proposera alors des méthodes à des composants qui n'en ont pas besoin du tout, et je sais pas si poser des "friend" partout est une bonne solution.

+ Répondre à la discussion
Cette discussion est résolue.

Discussions similaires

  1. [WD18] Ordre d'initialisation des champs
    Par tAKAmAkA dans le forum WinDev
    Réponses: 2
    Dernier message: 02/10/2014, 17h14
  2. Ordre d'initialisation des attributs
    Par Iradrille dans le forum C++
    Réponses: 7
    Dernier message: 20/02/2013, 03h34
  3. Réponses: 16
    Dernier message: 09/08/2010, 11h14
  4. Réponses: 9
    Dernier message: 21/03/2010, 21h42
  5. [] Tri d'un tableau par ordre alphabétique
    Par cafeine dans le forum VB 6 et antérieur
    Réponses: 3
    Dernier message: 17/09/2002, 08h43

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo