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 :

questions sur la liste d'initilialisation du constructeur


Sujet :

Langage C++

  1. #1
    r0d
    r0d est déconnecté
    Expert éminent

    Homme Profil pro
    tech lead c++ linux
    Inscrit en
    Août 2004
    Messages
    4 262
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Ain (Rhône Alpes)

    Informations professionnelles :
    Activité : tech lead c++ linux

    Informations forums :
    Inscription : Août 2004
    Messages : 4 262
    Points : 6 680
    Points
    6 680
    Billets dans le blog
    2
    Par défaut questions sur la liste d'initilialisation du constructeur
    Bonjour à tous,

    dans cette FAQ, il est écrit que:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    Fred::Fred() : x_() 
    { 
        x_ = n'importe quoi;
    }
    Dans ce cas, l'expression n'importe quoi provoque la création d'un objet temporaire
    Etant sous-entendu que
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    Fred::Fred(valeur) : x_(valeur) 
    { 
    }
    Ne le fait pas.

    1ere question:
    Les 2 codes suivants sont-ils équivalent (est-ce que ce sera le même code ASM généré):
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    Fred::Fred() : x_() // avec liste d'initialisation, mais vide
    { 
        x_ = n'importe quoi; // et une affectation
    }
    et
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    Fred::Fred() // sans liste d'initialisation
    { 
        x_ = n'importe quoi; // et une affectation 
    }


    2eme question
    Connaissez-vous des cas précis où les 2 codes suivants ne sont pas identiques (pas le même code ASM généré):
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    Fred::Fred()  // sans liste d'initialisation
    { 
        x_ = n'importe quoi;
    }
    et
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    Fred::Fred(valeur) : x_(valeur)    // avec liste d'initialisation
    { 
    }
    Parce que ça fait un moment que je cherche, avec des types composés et tout, mais à chaque fois l'asm est le même.
    « L'effort par lequel toute chose tend à persévérer dans son être n'est rien de plus que l'essence actuelle de cette chose. »
    Spinoza — Éthique III, Proposition VII

  2. #2
    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
    Points : 13 017
    Points
    13 017
    Par défaut
    Citation Envoyé par r0d Voir le message
    1ere question:
    Les 2 codes suivants sont-ils équivalent (est-ce que ce sera le même code ASM généré):
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    Fred::Fred() : x_() // avec liste d'initialisation, mais vide
    { 
        x_ = n'importe quoi; // et une affectation
    }
    et
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    Fred::Fred() // sans liste d'initialisation
    { 
        x_ = n'importe quoi; // et une affectation 
    }
    Il y a peut être une subtilité qui m'échappe pour un membre qui ne serait pas un type de base, mais si tu ne spécifie pas x_(), c'est le constructeur par défaut qui est appelé... donc je dirais _x().
    Il y a une différence si x_ est un type de base :
    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
     
    struct A
    {
       A()
       {}
       int m_i;
    };
     
    struct B
    {
       B()
       :m_i()
       {}
       int m_i;
    };
    int main()
    {
       A a;
       B b;
    }
    a.m_i est non initialisé alors que b.m_i est initialisé à une valeur par défaut (zéro).

    Citation Envoyé par r0d Voir le message
    2eme question
    Connaissez-vous des cas précis où les 2 codes suivants ne sont pas identiques (pas le même code ASM généré):
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    Fred::Fred()  // sans liste d'initialisation
    { 
        x_ = n'importe quoi;
    }
    et
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    Fred::Fred(valeur) : x_(valeur)    // avec liste d'initialisation
    { 
    }
    Parce que ça fait un moment que je cherche, avec des types composés et tout, mais à chaque fois l'asm est le même.
    x_ supporte la copie mais pas l'affectation : le second marche mais pas le premier.
    Si x_ redéfinit l'opérateur = et le constructeur par copie alors tu devrais avoir deux codes différents, non ?

  3. #3
    Membre émérite

    Inscrit en
    Mai 2008
    Messages
    1 014
    Détails du profil
    Informations forums :
    Inscription : Mai 2008
    Messages : 1 014
    Points : 2 252
    Points
    2 252
    Par défaut
    Citation Envoyé par r0d Voir le message
    1ere question:
    Les 2 codes suivants sont-ils équivalent (est-ce que ce sera le même code ASM généré):
    Oui.
    Un appel au constructeur par défaut, puis un appel à l'opérateur=.

    Citation Envoyé par r0d Voir le message
    2eme question
    Connaissez-vous des cas précis où les 2 codes suivants ne sont pas identiques (pas le même code ASM généré):
    Dès que le constructeur par défaut ou l'opérateur= est suffisament complexe pour dérouter l'optimiseur. Ce qui peut arriver très vite :
    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
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
     
    #include <string>
    #include <iostream>
     
    struct Human
    {
        Human():
        name("John Doe"),
        id(492)
        {
        }
     
        Human(const std::string& name_, int id_):
        name(name_),
        id(id_)
        {
        }
     
        std::string name;
        int id;
    };
     
    struct B
    {
        B(const Human& a):a_(a)
        {
        }
     
        Human a_;
    };
     
    struct C
    {
        C(const Human& a)
        {
            a_ = a;
        }
     
        Human a_;
    };
     
    int main()
    {
        Human a("Name", 5);
     
        B b(a);
        C c(a);
    }
    J'obtiens l'ASM suivant pour la construction de b avec vs2005 :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
     
    B b(a);
    004010EF  lea         ecx,[esp+28h] 
    004010F3  push        ecx  
    004010F4  lea         ecx,[esp+4Ch] 
    004010F8  call        dword ptr [__imp_std::basic_string<char,std::char_traits<char>,std::allocator<char> >::basic_string<char,std::char_traits<char>,std::allocator<char> > (402044h)] 
    004010FE  mov         edx,dword ptr [esp+44h] 
    00401102  mov         dword ptr [esp+64h],edx
    Le constructeur est inliné.
    alors que pour c :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
     
    C c(a);
    00401106  lea         eax,[esp+68h] 
    0040110A  push        eax  
    0040110B  lea         edi,[esp+2Ch] 
    0040110F  mov         byte ptr [esp+9Ch],3 
    00401117  call        C::C (401000h) 
    0040111C  mov         byte ptr [esp+98h],4
    Le constructeur n'est plus inliné et son ASM est :
    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
    28
     
    struct C
    {
        C(const Human& a)
    00401010  push        0FFFFFFFFh 
    00401012  push        offset __ehhandler$??0C@@QAE@ABUHuman@@@Z (401968h) 
    00401017  mov         eax,dword ptr fs:[00000000h] 
    0040101D  push        eax  
    0040101E  push        esi  
    0040101F  mov         eax,dword ptr [___security_cookie (403000h)] 
    00401024  xor         eax,esp 
    00401026  push        eax  
    00401027  lea         eax,[esp+8] 
    0040102B  mov         dword ptr fs:[00000000h],eax 
    00401031  mov         esi,dword ptr [esp+18h] 
    00401035  push        offset string "John Doe" (402114h) 
    0040103A  mov         ecx,esi 
    0040103C  call        dword ptr [__imp_std::basic_string<char,std::char_traits<char>,std::allocator<char> >::basic_string<char,std::char_traits<char>,std::allocator<char> > (402040h)] 
    00401042  mov         dword ptr [esi+1Ch],1ECh 
        {
            a_ = a;
    00401049  push        edi  
    0040104A  mov         ecx,esi 
    0040104C  mov         dword ptr [esp+14h],0 
    00401054  call        dword ptr [__imp_std::basic_string<char,std::char_traits<char>,std::allocator<char> >::operator= (402044h)] 
    0040105A  mov         eax,dword ptr [edi+1Ch] 
    0040105D  mov         dword ptr [esi+1Ch],eax 
        }

  4. #4
    Expert éminent sénior
    Avatar de koala01
    Homme Profil pro
    aucun
    Inscrit en
    Octobre 2004
    Messages
    11 614
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 52
    Localisation : Belgique

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 614
    Points : 30 626
    Points
    30 626
    Par défaut
    Salut,

    La journée fut longue et il se fait tard, donc je vais sans doute répondre à coté, mais, il faut garder en mémoire que, si une liste d'initialisation est vide et qu'il existe un constructeur par défaut (trivial), ce sera celui-là qui sera appelé quoi qu'il advienne

    Ce ne peut en effet pas être le constructeur par copie qui sera appelé, parce que... le compilateur n'a aucun moyen de savoir quel argument doit être copié pour créer quel membre (sous entendu, lorsque plusieurs membres sont de type identique ou similaire ).

    Qu'il te "suffise" d'annuler l'implémentation du constructeur trivial en déclarant un argument quelconque dans le constructeur du membre pour t'en convaincre: le compilateur te "jettera" sans l'ombre d'un remord

    Et cela nous ramène tout droit vers la nécessité de s'interroger sur l'opportunité de laisser un constructeur trivial, sachant que ce que l'on attend des constructeur est... de fournir un objet correctement initialisé.

    Bien sur, il y a des cas dans lesquels c'est pleinement justifié, mais dans d'autres, il y a un grand intérêt à... s'abstenir
    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

  5. #5
    Expert éminent sénior
    Avatar de Médinoc
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Septembre 2005
    Messages
    27 369
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 40
    Localisation : France

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Septembre 2005
    Messages : 27 369
    Points : 41 519
    Points
    41 519
    Par défaut
    Pour la réponse à la première question, il me semble qu'il y a une subtilité pour le cas des types POD (Plain Old Data).

    Si je me souviens bien, les types POD sont initialisés à zéro si leur constructeur par défaut est appelé explicitement, mais restent non-initialisés dans le cas contraire...
    SVP, pas de questions techniques par MP. Surtout si je ne vous ai jamais parlé avant.

    "Aw, come on, who would be so stupid as to insert a cast to make an error go away without actually fixing the error?"
    Apparently everyone.
    -- Raymond Chen.
    Traduction obligatoire: "Oh, voyons, qui serait assez stupide pour mettre un cast pour faire disparaitre un message d'erreur sans vraiment corriger l'erreur?" - Apparemment, tout le monde. -- Raymond Chen.

  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
    Points : 13 017
    Points
    13 017
    Par défaut
    Citation Envoyé par Médinoc Voir le message
    Pour la réponse à la première question, il me semble qu'il y a une subtilité pour le cas des types POD (Plain Old Data).

    Si je me souviens bien, les types POD sont initialisés à zéro si leur constructeur par défaut est appelé explicitement, mais restent non-initialisés dans le cas contraire...
    J'ai l'impression que ça s'est perdu dans ma réponse :
    Citation Envoyé par 3DArchi Voir le message
    Il y a une différence si x_ est un type de base :
    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
     
    struct A
    {
       A()
       {}
       int m_i;
    };
     
    struct B
    {
       B()
       :m_i()
       {}
       int m_i;
    };
    int main()
    {
       A a;
       B b;
    }
    a.m_i est non initialisé alors que b.m_i est initialisé à une valeur par défaut (zéro).

  7. #7
    Membre émérite

    Inscrit en
    Mai 2008
    Messages
    1 014
    Détails du profil
    Informations forums :
    Inscription : Mai 2008
    Messages : 1 014
    Points : 2 252
    Points
    2 252
    Par défaut
    Woa c'est subtil en effet, je n'avais jamais remarqué ce comportement. J'ai pris l'habitude de toujours initialiser les POD dans un constructeur par défaut comme ceci :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
     
    S::S():
    entier(0),
    flottant(0.0f),
    double(0.0)
    {
    }
    Mais donc en fait on a droit à ce petit raccourci syntaxique ?
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
     
    S::S():
    entier(),
    flottant(),
    double()
    {
    }
    Ce comportement est garantit ?

    Edit : ça marche aussi avec les tableaux !?

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
     
    struct S
    {
        S():tab()
        {
        }
     
        int tab[50];
    };
     
    int main()
    {
      S s;
    }
    vs2005 avertit : "warning C4351: new behavior: elements of array 'S::tab' will be default initialized"

  8. #8
    Membre chevronné
    Avatar de Goten
    Profil pro
    Inscrit en
    Juillet 2008
    Messages
    1 580
    Détails du profil
    Informations personnelles :
    Âge : 33
    Localisation : France

    Informations forums :
    Inscription : Juillet 2008
    Messages : 1 580
    Points : 2 205
    Points
    2 205
    Par défaut
    Ben si je fais l'analogie avec le cas des templates :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    template <typename T>
    void foo()
    {
         T t = T(); // garantie que les types POD seront default-initialisé
    }
    Alors oui c'est autorisé, puisque une fois le template instancié la syntaxe est là même.
    "Hardcoded types are to generic code what magic constants are to regular code." --A. Alexandrescu

  9. #9
    Expert éminent sénior
    Avatar de koala01
    Homme Profil pro
    aucun
    Inscrit en
    Octobre 2004
    Messages
    11 614
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 52
    Localisation : Belgique

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 614
    Points : 30 626
    Points
    30 626
    Par défaut
    Citation Envoyé par Arzar Voir le message
    Woa c'est subtil en effet, je n'avais jamais remarqué ce comportement. J'ai pris l'habitude de toujours initialiser les POD dans un constructeur par défaut comme ceci :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
     
    S::S():
    entier(0),
    flottant(0.0f),
    double(0.0)
    {
    }
    Mais donc en fait on a droit à ce petit raccourci syntaxique ?
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
     
    S::S():
    entier(),
    flottant(),
    double()
    {
    }
    Ce comportement est garantit ?
    Oui...
    Edit : ça marche aussi avec les tableaux !?

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
     
    struct S
    {
        S():tab()
        {
        }
     
        int tab[50];
    };
     
    int main()
    {
      S s;
    }
    vs2005 avertit : "warning C4351: new behavior: elements of array 'S::tab' will be default initialized"
    Re oui, pour autant qu'il s'agisse d'un tableau dont la taille est statique

    Autrement, c'est le pointeur sur le premier élémentqui sera zero-initialisé et qui vaudra donc NULL (s'il l'est ).

    La zero initialisation apparait déjà dans la norme modifiée lors de TR1... Ce qui ne veut pas dire qu'elle n'apparaisse pas plus tot
    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

  10. #10
    Membre émérite

    Inscrit en
    Mai 2008
    Messages
    1 014
    Détails du profil
    Informations forums :
    Inscription : Mai 2008
    Messages : 1 014
    Points : 2 252
    Points
    2 252
    Par défaut
    Merci !

  11. #11
    r0d
    r0d est déconnecté
    Expert éminent

    Homme Profil pro
    tech lead c++ linux
    Inscrit en
    Août 2004
    Messages
    4 262
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Ain (Rhône Alpes)

    Informations professionnelles :
    Activité : tech lead c++ linux

    Informations forums :
    Inscription : Août 2004
    Messages : 4 262
    Points : 6 680
    Points
    6 680
    Billets dans le blog
    2
    Par défaut
    Okay, merci pour vos réponses

    @Arzar: dans l'asm de ton post, il m'a semblé que ce qu'il sa passe, ce n'est pas que le constructeur de Human est inliné, mais c'est qu'il n'est tout simplement pas appelé. Je me demande si ce n'est pas plus une particularité du compilateur et de sa façon d'optimiser, que d'un comportement conforme à la norme.
    « L'effort par lequel toute chose tend à persévérer dans son être n'est rien de plus que l'essence actuelle de cette chose. »
    Spinoza — Éthique III, Proposition VII

  12. #12
    Membre émérite

    Inscrit en
    Mai 2008
    Messages
    1 014
    Détails du profil
    Informations forums :
    Inscription : Mai 2008
    Messages : 1 014
    Points : 2 252
    Points
    2 252
    Par défaut
    C'est fort possible, je suis une grosse buse en assembleur

    J'avais interprété comme ceci (en supprimant tous les push, lea et autres mov intermédiaires et en collapsant toutes les templates pour la lisibilité)

    Dans le constructeur de B :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
     
    // B b(a); devient en gros
    call   dword ptr [__imp_std::string::string (402044h)]
    Il me semble que c'est l'appel au constructeur de string prenant un char*, auquel le compilateur passe la chaine "John Doe" stockée à l'adresse 402044h

    Et dans celui de C :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
     
    struct C
    {
        C(const Human& a)
        call  dword ptr [__imp_std::string::string(402040h)] 
        {
    Avant même de rentrer dans la fonction on fait un appel au constructeur de string prenant un char* en passant "John Doe". C'est ce fameux temporaire crée pour rien si on n'utilise pas liste d'initialisation.
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
     
         {
            // a_ = a; devient
            call dword ptr [__imp_std::string::operator= (402044h)] 
        }
    Puis on appelle l'opérateur= qui prend un char*, ici la chaine "Name" stockée en 402044h.

    C'est pas trop n'importe quoi ?

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

Discussions similaires

  1. questions sur les listes
    Par blaise4714 dans le forum Général Python
    Réponses: 5
    Dernier message: 06/06/2008, 03h40
  2. Question sur les listes d'affichage
    Par brouss dans le forum OpenGL
    Réponses: 3
    Dernier message: 08/03/2007, 12h56
  3. question sur les listes/set/vector
    Par deubelte dans le forum SL & STL
    Réponses: 11
    Dernier message: 04/01/2007, 20h41
  4. question sur les listes/set/vector
    Par deubelte dans le forum SL & STL
    Réponses: 16
    Dernier message: 28/12/2006, 20h17
  5. des questions sur les listes chainées
    Par hunter99 dans le forum C
    Réponses: 13
    Dernier message: 05/12/2006, 22h51

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