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

C++ Discussion :

Liste d'initialisation C++


Sujet :

C++

  1. #1
    Membre du Club
    Profil pro
    Inscrit en
    Janvier 2008
    Messages
    89
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Janvier 2008
    Messages : 89
    Points : 50
    Points
    50
    Par défaut Liste d'initialisation C++
    Bonjour,
    Je cherche un tuto/exemple qui expliquerait le pourquoi du comment et le bien fondé des listes d'initialisation. De façon assez curieuse, il semble qu'il soit toujours recommandé de les utiliser et pourtant la plupart des exemples de code que l'on trouve sur le net ne le font pas.
    Cdlt, Christian

  2. #2
    Membre expert
    Profil pro
    Inscrit en
    Mars 2007
    Messages
    1 415
    Détails du profil
    Informations personnelles :
    Localisation : France, Paris (Île de France)

    Informations forums :
    Inscription : Mars 2007
    Messages : 1 415
    Points : 3 156
    Points
    3 156
    Par défaut
    Hello

    Il n'y a pas besoin d'un tuto très long pour expliquer pourquoi. Si tu "initialises" tes variables dans le code du constructeur, elles sont en fait déjà initialisées et tu ne fais que leur affecter une nouvelle valeur.

    Cela se voit très bien avec les références qui doivent obligatoirement être initialisées. Le code suivant ne compile pas:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    class refholder
    {
        int & ref_;
     
    public:
        refholder(int & ref)
        {
            ref_ = ref;
        }
    };
     
    int main() { return 0; }
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    test.cpp: In constructor 'refholder::refholder(int&)':
    test.cpp:6: error: uninitialized reference member 'refholder::ref_'
    Alors que ce code est correct:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    class refholder
    {
        int & ref_;
     
    public:
        refholder(int & ref) : ref_(ref)
        {}
    };
     
    int main() { return 0; }
    Cela sera également valable pour les membres qui n'ont pas de constructeur par défaut. Code incorrect:

    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
    class A
    {
        int i_;
     
    public:
        A(int & i) : i_(i)
        {}
    };
     
    class B
    {
        A a_;
     
    public:
        B(int & b)
        {
            a_ = A(b);
        }
    };
     
    int main() { return 0; }
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    test.cpp: In constructor 'B::B(int&)':
    test.cpp:16: error: no matching function for call to 'A::A()'
    test.cpp:6: note: candidates are: A::A(int&)
    test.cpp:2: note:                 A::A(const A&)
    Version correcte :

    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
    class A
    {
        int i_;
     
    public:
        A(int & i) : i_(i)
        {}
    };
     
    class B
    {
        A a_;
     
    public:
        B(int & b) : a_(b)
        {}
    };
     
    int main() { return 0; }
    Il est donc nettement plus propre d'utiliser les listes d'initialisation. A noter qu'avec C++11, on peut également initialiser les membres au niveau de leur déclaration dans le header (ce qui est intéressant pour les membres simples comme les types de base ou les pointeurs, à éviter avec des types plus complexes pour limiter les dépendances du header).

    Enfin, la liste d'initialisation est le seul moyen d'appeler les constructeurs parents avec des paramètres.
    Find me on github

  3. #3
    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
    Citation Envoyé par coberle Voir le message
    la plupart des exemples de code que l'on trouve sur le net ne le font pas.
    C'est que vous cherchez mal. L'utilisation de la liste d'initialisation de façon systématique est admise par la communauté depuis bien longtemps, et c'est donc le cas de tout bon code que l'on trouve sur internet.

    Un peu de littérature:
    http://www.parashift.com/c++-faq-lite/init-lists.html
    http://forums.codeguru.com/showthrea...hould-I-use-it

    Un peu plus avancé, pour le problème des exceptions:
    http://www.gotw.ca/gotw/066.htm
    « 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

  4. #4
    Expert éminent sénior

    Femme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Juin 2007
    Messages
    5 189
    Détails du profil
    Informations personnelles :
    Sexe : Femme
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels

    Informations forums :
    Inscription : Juin 2007
    Messages : 5 189
    Points : 17 141
    Points
    17 141
    Par défaut
    Ne serait-ce pas une confusion avec les std::initializer-list?
    Mes principes de bases du codeur qui veut pouvoir dormir:
    • Une variable de moins est une source d'erreur en moins.
    • Un pointeur de moins est une montagne d'erreurs en moins.
    • Un copier-coller, ça doit se justifier... Deux, c'est un de trop.
    • jamais signifie "sauf si j'ai passé trois jours à prouver que je peux".
    • La plus sotte des questions est celle qu'on ne pose pas.
    Pour faire des graphes, essayez yEd.
    le ter nel est le titre porté par un de mes personnages de jeu de rôle

  5. #5
    Nouveau membre du Club
    Homme Profil pro
    Etudiant en génie mécanique
    Inscrit en
    Mars 2011
    Messages
    146
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Suisse

    Informations professionnelles :
    Activité : Etudiant en génie mécanique

    Informations forums :
    Inscription : Mars 2011
    Messages : 146
    Points : 33
    Points
    33
    Par défaut
    L'exemple de la liste d'initialisation dans le cas d'une référence est une exception: on ne peut initialiser une référence que de cette manière.
    Ca ne rend pas la liste d'initialisation plus valable dans le cas des autres variables. Est-ce qu'on y gagne quelque chose en termes de performances?
    Personnellement, je renonce parfois à la liste d'initialisation quand, pour des raisons de lisibilité, je veux regrouper certaines intialisations avec leur traitement (toujours dans le constructeur) s'il y en a.
    C'est pas plus intelligent d'utiliser les deux solutions de manière pragmatique?

  6. #6
    Membre éclairé

    Profil pro
    Inscrit en
    Décembre 2013
    Messages
    393
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Décembre 2013
    Messages : 393
    Points : 685
    Points
    685
    Par défaut
    Citation Envoyé par jblecanard Voir le message
    Si tu "initialises" tes variables dans le code du constructeur, elles sont en fait déjà initialisées et tu ne fais que leur affecter une nouvelle valeur.
    (HS : tu gagnes un deuxième trophet "détérage de post" - mais plus petit que le premier)

    La phrase de jblecanard est pourtant claire : si tu n'utilises pas la liste d'initialisation, ce n'est pas une initialisation mais une affectation. Tu initialises une première fois tes variables, puis tu les modifies.

    Donc c'est une mauvaise pratique et un problème de performance de ne pas utiliser les listes d'initilisation

  7. #7
    Rédacteur/Modérateur
    Avatar de JolyLoic
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Août 2004
    Messages
    5 463
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 49
    Localisation : France, Yvelines (Île de France)

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

    Informations forums :
    Inscription : Août 2004
    Messages : 5 463
    Points : 16 213
    Points
    16 213
    Par défaut
    Citation Envoyé par Armulis Voir le message
    Personnellement, je renonce parfois à la liste d'initialisation quand, pour des raisons de lisibilité, je veux regrouper certaines intialisations avec leur traitement (toujours dans le constructeur) s'il y en a.
    Est-ce que tu peux donner un exemple de ce que tu entends par là ?
    Parce que depuis qu'il y a les delegating constructors, qui s'ajoutent à la possibilité d'initialiser à l'aide d'une fonction, j'ai du mal à voir des cas où l'écriture d'une liste d'une liste d'initialisation est plus complexe que celle du body d'un constructeur.

    Le seul cas que je vois comme ça, c'est si tu as envie d'initialiser de manière non triviale deux champs de ta classe à partir d'un même paramètre du constructeur (et que ces deux champs n'ont pas intérêt à être externalisés dans une classe dédiée).
    Ma session aux Microsoft TechDays 2013 : Développer en natif avec C++11.
    Celle des Microsoft TechDays 2014 : Bonnes pratiques pour apprivoiser le C++11 avec Visual C++
    Et celle des Microsoft TechDays 2015 : Visual C++ 2015 : voyage à la découverte d'un nouveau monde
    Je donne des formations au C++ en entreprise, n'hésitez pas à me contacter.

  8. #8
    Membre expert
    Profil pro
    Inscrit en
    Mars 2007
    Messages
    1 415
    Détails du profil
    Informations personnelles :
    Localisation : France, Paris (Île de France)

    Informations forums :
    Inscription : Mars 2007
    Messages : 1 415
    Points : 3 156
    Points
    3 156
    Par défaut
    Citation Envoyé par Armulis Voir le message
    Ca ne rend pas la liste d'initialisation plus valable dans le cas des autres variables. Est-ce qu'on y gagne quelque chose en termes de performances?
    C'est pourtant une question de bon sens : vaut-il mieux seulement initialiser une variable ou bien l'initialiser puis lui affecter une valeur ? Dans le cas des entiers et pointeurs, il est vrai que la différence est faible voire nulle puisque l'initialisation par défaut n'affecte pas la valeur. En revanche pour d'autres variables, oui, on gagne en performance. La preuve :

    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
    #include <vector>
    #include <chrono>
    #include <iostream>
     
    using collec_type = std::vector<int>;
     
    class A {
      collec_type v_;
     public:
      A() : v_ {1,2,4,4,5,6,7,8,9} {}
    };
     
    class B {
      collec_type v_;
     public:
      B() { v_ = collec_type({1,2,4,4,5,6,7,8,9}); }
    };
     
    int main() {
      using namespace std::chrono;
      constexpr size_t nb_iter = 1e7; 
     
      auto t1 = high_resolution_clock::now();
      for(size_t i = 0; i < nb_iter; ++i)
        A a;
      auto t2 = high_resolution_clock::now();
      for(size_t i = 0; i < nb_iter; ++i) 
        B b;
      auto t3 = high_resolution_clock::now();
     
      std::cout << "Init list: " << duration_cast<milliseconds>(t2-t1).count() << "ms\n";
      std::cout << "Ctor body: " << duration_cast<milliseconds>(t3-t2).count() << "ms\n";
     
    }
    Seule différence entre A et B : A initiliase via une liste d'initialisation et B via une affectation dans le corps du constructeur. En debug sur la machine que j'utilise actuellement, la différence est frappante :

    GCC 4.9, debug

    Init list: 2582ms
    Ctor body: 4833ms
    Clang 3.5, debug

    Init list: 2128ms
    Ctor body: 3672ms
    En optilmisé (-O2), je porte le nombre d'itérations à 1e8 car un effet de bord peut faire croire que la version liste d'initialisation est plus lente de quelques millisecondes. On obtient :

    GGC 4.9, O2

    Init list: 3275ms
    Ctor body: 3797ms
    Clang 3.5, 02

    Init list: 3257ms
    Ctor body: 3755ms
    Bien sûr ces résultats se reproduisent sur des runs successifs. En -O3, il n'y a pas de différence significative, le test est trop simple et les compilos optimisent les deux objets de manière similaire. Quoi qu'il en soit, ne pas utiliser les listes d'initialisation délibérément sans que cela ne se justifie fonctionnellement est une mauvaise habitude à perdre.
    Find me on github

  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
    Salut,
    Citation Envoyé par Armulis Voir le message
    L'exemple de la liste d'initialisation dans le cas d'une référence est une exception: on ne peut initialiser une référence que de cette manière.
    Ca ne rend pas la liste d'initialisation plus valable dans le cas des autres variables. Est-ce qu'on y gagne quelque chose en termes de performances?
    Personnellement, je renonce parfois à la liste d'initialisation quand, pour des raisons de lisibilité, je veux regrouper certaines intialisations avec leur traitement (toujours dans le constructeur) s'il y en a.
    C'est pas plus intelligent d'utiliser les deux solutions de manière pragmatique?
    En un mot : non!!!

    De manière générale, tu devrais toujours utiliser la liste d'initialisation, point barre. Si tu as "quelque chose" à faire en plus dans ton constructeur, fait le, mais pourquoi voudrais tu renoncer à la liste d'initialisation alors que, au final, c'est la seule méthode qui fonctionne de manière systématique, avec des valeurs et avec des références, et qui évite, de plus, des copies inutiles parfois très lourdes en ressources

    De plus, on peut également citer le cas des classes ayant sémantique d'entité qui ne sont, typiquement, ni copiables ni affectables et pour lesquelles ont peu malgré tout d'envisager l'utilisation sous la forme de membre créé par valeur et non de membre prenant la forme de références ou de pointeurs... La liste d'initialisation sera, là encore, la seule solution envisageable !!!
    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
    class Base{
        public:
            virtual ~Base();
            Base(Base const &) = delete;
            Base & opertor = (Base const &) = delete;
    };
    class Derivee : public Base{
    public:
        Derivee():Derivee(0){}
        Derivee(int value):value_(value){}
    private:
        int value_;    
    };
    class Utilisatrice{
    public:
        Utilisatrice(int i):d_(i){} // OK: constructeur prenant un int est appelé
        /*
        Utilisatrice(int i){
            d_ = Derivee(i); // erreur: copie et affectation interdite
        }
        */
    private:
        Derivee d_;
    };
    Note : ce n'est pas parce que Derivee hérite de Base que tu ne peut envisager que l'utilisation d'un pointeur sur le type Base et recourir systématiquement à l'allocation dynamique de la mémoire : si tu sais pertinemment bien que tu as besoin d'un objet de type Derivee, il n'y a rien --hormis le fonctionnement de certaines bibliothèque -- qui puisse t'interdire de créer une instance "classique" de ta classe
    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
    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
    En gros, l'idée générale de cette liste d'initialisation, c'est comme pour le mot-clé auto: "toute chose égale par ailleurs".
    L'idée étant que ce n'est jamais pire d'utiliser la liste d'initialisation que de le corps du constructeur, alors autant le faire tout le temps.
    « 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

Discussions similaires

  1. Liste d'initialisation en java ?
    Par jph_void dans le forum Langage
    Réponses: 12
    Dernier message: 13/05/2008, 09h44
  2. Réponses: 4
    Dernier message: 20/04/2008, 20h12
  3. Liste d'initialisation C++
    Par three minute hero dans le forum BOUML
    Réponses: 7
    Dernier message: 08/10/2007, 10h18
  4. FAQ -> liste d'initialisation
    Par Mindiell dans le forum C++
    Réponses: 24
    Dernier message: 05/09/2007, 17h24
  5. [syntax] liste d'initialisation et heritage
    Par ZaaN dans le forum C++
    Réponses: 1
    Dernier message: 12/12/2006, 16h01

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