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 :

références vs pointeurs Quand les utiliser ?


Sujet :

C++

  1. #1
    Membre actif Avatar de BioKore
    Homme Profil pro
    Dresseur d'Alpaga
    Inscrit en
    Septembre 2016
    Messages
    300
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Canada

    Informations professionnelles :
    Activité : Dresseur d'Alpaga

    Informations forums :
    Inscription : Septembre 2016
    Messages : 300
    Points : 219
    Points
    219
    Par défaut références vs pointeurs Quand les utiliser ?
    Bonjour à tous,

    Une question existentielle aujourd'hui : "quelle est la différence fondamentale entre les références et les pointeurs (nus ou 'intelligents') ?"
    Car autant j'utilise les références à foison, partout où je peux, autant je vois pas mal de gens qui utilisent encore les pointeurs (des gens qui semblent quand même savoir ce qu'ils font).
    Du coup je me demande, sous quelles conditions on peut estimer nécessaire d'utiliser des pointeurs plutôt que des références ?
    Peut-être ne s'agit-il que d'une question d'habitudes ou de goût ?

    Merci d'avance.

  2. #2
    Modérateur

    Avatar de Bktero
    Homme Profil pro
    Développeur en systèmes embarqués
    Inscrit en
    Juin 2009
    Messages
    4 481
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 36
    Localisation : France, Loire Atlantique (Pays de la Loire)

    Informations professionnelles :
    Activité : Développeur en systèmes embarqués

    Informations forums :
    Inscription : Juin 2009
    Messages : 4 481
    Points : 13 679
    Points
    13 679
    Billets dans le blog
    1
    Par défaut
    Mes rules of thumb :
    1. n'utilise pas de pointeur
    2. si tu n'as pas le choix, utilise des pointeurs.
    3. avant d'appliquer la règle 2, relis la règle 1


    Des cas où tu n'as pas le choix : interagir avec des API C, interagir avec des API C++ qui demandent des pointeurs, pointer vers une valeur optionnelle.


  3. #3
    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,

    Les règles citées par Bktero sont parfaites, j'aurais simplement énoncé la première sous la forme de "utilises les référence partout où tu peux, ou tu en as besoin"

    L'utilisation de pointeur ne doit se faire que si tu n'as vraiment pas d'autre choix, car elle te forcera à sécuriser au maximum les différents accès que tu tenteras de faire à l'élément pointé.

    Il y aura quatre raisons pour lesquelles tu peux ne pas avoir le choix.

    1- parce que tu veux représenter une donnée qui peut ne pas exister.

    Si tu veux représenter un graphe ou un arbre, par exemple, "il se peut" que tu éprouves le besoin de fournir une référence vers "le noeud parent" pour chaque noeud. Evidemment, le noeud racine n'aura pas de parent, et il faut donc pouvoir représenter le fait que la donnée n'existe pas

    Nous pourrions bien envisager d'utiliser std::optional depuis C++17, mais ce n'est pas prévu pour.

    2- lorsque tu dois t'interfacer avec une API écrite en C, car C ne dispose que des pointeurs et la notion de référence est inconnue en C.

    3- Lorsque tu veux manipuler des classes faisant partie d'une hiérarchie de classes, le tout, en les considérant comme si chaque instances étaient du type de base. Un petit exemple pour comprendre:

    Soient les classes
    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
    class Base{
    public:
        Base() = default;
        Base(Base const &) = delete;
        Base & operator=(Base const &) = delete;
        ~Base() = default;
        /* ... */
    };
    class Derivee1 : public Base{
        /* ... */
    }:
    class Derivee2 : public Base{
        /*...*/
    };
    /* ... */
    class DeriveeN : public Base{
        /* ... */
    };
    Plus loin, tu vas avoir un code qui créera une instance (qui "passe pour être du type de base") dont le type réel dépendra "des circonstances", par exemple
    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
    /* une enumeration permettant de savoir quel type de donnée créer */
    enum DataType{
        first,
        second,
        /* ... */
        enieme
    };
    /* j'utilise les pointeurs intelligents, parce qu'il faudrait le faire de manière systématique */
     
    std::unique_ptr<Base> createData(DataType dt /*, ... */){
        switch(dt){
            case first:
                return std::make_unique<Derivee1>();
            case second:
                return std::make_unique<Derivee2>();
            /* ... */
            case enieme:
                return std::make_unique<Derivee3>();
            default:
                assert(false && "You should never come here!");
        }
        return std::unique_ptr<Base>{nullptr};
    }
    (NOTA: je n'ai pas cherché à faire quelque chose de parfaitement correct, mais bien à te donner l'idée générale )

    Ou peut-être voudras tu -- simplement -- pouvoir maintenir (avec la notion de propriété qui va bien) un ensemble d'instances de classes dérivée de Base dans une collection spécifique

    Tu n'auras alors pas d'autre choix que de passer par l'allocation dynamique de la mémoire (et par l'utilisation des pointeurs intelligents, tant qu'à faire). Cela prendrait une forme proche de
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    std::vector<std::unique_ptr<Base>> tab;
    for(/*...*/ ){
        /* ... */
        tab.emplace(std::move(createData(/* ... */)));
    }
    Par contre, maintenant que tu es sur que chaque pointeur se trouvant dans le tableau existe -- vu que la mémoire qui lui est allouée est automatiquement libérée lorsque le pointeur intelligent est supprimé du tableau -- tu n'as plus aucune raison (en dehors du fait de transférer la propriété du pointeur à une fonction, s'entend) de transférer l'élément pointé par pointeur, car tu as la certitude qu'il existe.

    Tu pourrais donc avoir une fonction proche de
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    void foo(Base /* const*/ & b){
        /* utilise le polymorphisme :D */
    }
    qui serait appelée pour chaque élément de ton tableau sous une forme proche de
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    for(auto /* const */ & it: tab){ // it est une référence sur std::unique_ptr
        foo(*it.get()); // on fournit à foo "ce qui est pointé" par l'élément renvoyé par la fonction membre get()
    }
    4- La dernière raison est d'utiliser une bibliothèque externe qui est "prévue comme cela".

    La bibliothèque Qt, par exemple a été développée bien avant que les pointeurs intelligents ne soient mis au point. Elle a mis au point un système "parent / enfants" par lequel, dés que tu définis un élément (une boite de dialoguqe, par exemple) comme le "parent" d'un autre (un bouton, par exemple), la libération de la mémoire allouée au parent provoque automatiquement la libération de la mémoire allouée à tous ses enfants.

    Il est donc "normal" de traiter les pointeurs "assez légèrement" avec cette bibliothèque, pour la simple et bonne raison qu'ils sont traités "de manière transparente" par la bibliothèque elle-même
    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

  4. #4
    Membre actif Avatar de BioKore
    Homme Profil pro
    Dresseur d'Alpaga
    Inscrit en
    Septembre 2016
    Messages
    300
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Canada

    Informations professionnelles :
    Activité : Dresseur d'Alpaga

    Informations forums :
    Inscription : Septembre 2016
    Messages : 300
    Points : 219
    Points
    219
    Par défaut
    Bonjour, et merci pour ces retours.

    Je suis entièrement d'accord avec les règles que vos citez. Techniquement, cela fait un bon bout de temps que je n'ai pas eu à utiliser de pointeurs.
    Avec les cas cités par Koala1, globalement, je m’aperçois qu'il doit être tout de même relativement rare d'avoir à en passer par les pointeurs (dans le cas de la création d'un programme sans bibliothèque tierce).

    Merci pour vos retours. Dans tous les cas que j'ai rencontré jusqu'à présent donc, inutile pour moi d'utiliser les pointeurs.

  5. #5
    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 BioKore Voir le message
    globalement, je m’aperçois qu'il doit être tout de même relativement rare d'avoir à en passer par les pointeurs (dans le cas de la création d'un programme sans bibliothèque tierce).
    C'est, à vrai dire, beaucoup plus complexe que cela n'en a l'air, car cela dépendra énormément du domaine dans lequel tu travaille.

    Si tu travailles sur une application qui fait des math avancées, tu devrais pouvoir t'en passer "assez facilement" (si l'on décide d'ignorer que la plupart des collections utilisent des pointeurs en interne )

    Si tu travailles sur une bibliothèque graphique, ou -- tout simplement -- si tu as des "messieurs" et des "mesdames" qui doivent tous les deux être considérés comme "des gens" (sans distinction d'age ou de sexe), l'utilisation de pointeurs deviendra rapidement indispensable
    Merci pour vos retours. Dans tous les cas que j'ai rencontré jusqu'à présent donc, inutile pour moi d'utiliser les pointeurs.
    Tu as sans doute eu beaucoup de (mal) chance
    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

  6. #6
    Membre actif Avatar de BioKore
    Homme Profil pro
    Dresseur d'Alpaga
    Inscrit en
    Septembre 2016
    Messages
    300
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Canada

    Informations professionnelles :
    Activité : Dresseur d'Alpaga

    Informations forums :
    Inscription : Septembre 2016
    Messages : 300
    Points : 219
    Points
    219
    Par défaut
    On dira simplement que les sujets étaient "simples".

    Cependant, je vois effectivement quelques cas d'application dans lesquels l'utilisation des pointeurs simplifie la tâche.
    Une simple fonction d'initialisation / paramétrage de membre dans une classe partagée et, effectivement, l'utilisation des références s'avère plus difficilement praticable.

    Mais au moins je visualise mieux les conditions d'application des pointeurs et le "pourquoi".

    Merci encore.

  7. #7
    Rédacteur/Modérateur


    Homme Profil pro
    Network game programmer
    Inscrit en
    Juin 2010
    Messages
    7 115
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 36
    Localisation : Canada

    Informations professionnelles :
    Activité : Network game programmer

    Informations forums :
    Inscription : Juin 2010
    Messages : 7 115
    Points : 32 967
    Points
    32 967
    Billets dans le blog
    4
    Par défaut
    Personnellement, depuis C++11
    - référence : aucun changement, référence une donnée existante et ne peut pas changer qui est visé
    - pointeur : pointer vers une donnée optionnelle ou qui peut être repointée ailleurs

    Pour tout ce qui est ownership, pointeur intelligent. unique_ptr dans 99% des cas, je n'ai jamais eu besoin de shared_ptr à date.
    Pensez à consulter la FAQ ou les cours et tutoriels de la section C++.
    Un peu de programmation réseau ?
    Aucune aide via MP ne sera dispensée. Merci d'utiliser les forums prévus à cet effet.

  8. #8
    Membre actif Avatar de BioKore
    Homme Profil pro
    Dresseur d'Alpaga
    Inscrit en
    Septembre 2016
    Messages
    300
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Canada

    Informations professionnelles :
    Activité : Dresseur d'Alpaga

    Informations forums :
    Inscription : Septembre 2016
    Messages : 300
    Points : 219
    Points
    219
    Par défaut
    Ok merci de la précision. Je dois effectivement me renseigner sur les différences "Unique / shared", mais j'imagine que cette dernière correspond justement au ownership (shared_ptr peut avoir plusieurs propriétaires et pas unique_ptr j'imagine)..

    Mais, je profite de cette discussion pour m’écarter un tout petit peu du sujet, comment sont allouées les tableaux de pointeurs ? est-ce que la contiguïté des données est respectée ? Car un pointeur ne correspond pas à la donnée brute.
    Par exemple, si je créé un pool d'objet du type : [/c]std::vector<std::shared_ptr<int> >[/c] (ici shared ou unique, peu importe) est-ce que les X ou Y valeurs pointées chacune par les pointeurs du tableau sont contiguës ? Je pense que oui étant donné que j'ai pu voir certaines implémentations de pool un peu de cette forme mais je préfère m'en assurer.

  9. #9
    Expert éminent sénior
    Homme Profil pro
    Analyste/ Programmeur
    Inscrit en
    Juillet 2013
    Messages
    4 630
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Bouches du Rhône (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Analyste/ Programmeur

    Informations forums :
    Inscription : Juillet 2013
    Messages : 4 630
    Points : 10 556
    Points
    10 556
    Par défaut
    Citation Envoyé par BioKore Voir le message
    Car un pointeur ne correspond pas à la donnée brute.
    Certes , mais c'est juste une adresse - donc 1 entier de type intptr_t/ uintptr_t.

    Et pour les pointeurs intelligents , c'est juste "une structure" contenant toujours ce pointeur - mais peut y avoir 1 compteur et la liste des pointés (<- pour faire des liens bidirectionnels).
    Donc pas + compliqué qu'1 tableau C avec une "structure".

  10. #10
    Rédacteur/Modérateur


    Homme Profil pro
    Network game programmer
    Inscrit en
    Juin 2010
    Messages
    7 115
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 36
    Localisation : Canada

    Informations professionnelles :
    Activité : Network game programmer

    Informations forums :
    Inscription : Juin 2010
    Messages : 7 115
    Points : 32 967
    Points
    32 967
    Billets dans le blog
    4
    Par défaut
    vector assure que ses données sont contigües.
    Si tu stockes des pointeurs, les pointeurs seront contigüs, mais les données pointées ça va dépendre de l'allocateur utilisé pour les créer.
    Pensez à consulter la FAQ ou les cours et tutoriels de la section C++.
    Un peu de programmation réseau ?
    Aucune aide via MP ne sera dispensée. Merci d'utiliser les forums prévus à cet effet.

  11. #11
    Membre actif Avatar de BioKore
    Homme Profil pro
    Dresseur d'Alpaga
    Inscrit en
    Septembre 2016
    Messages
    300
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Canada

    Informations professionnelles :
    Activité : Dresseur d'Alpaga

    Informations forums :
    Inscription : Septembre 2016
    Messages : 300
    Points : 219
    Points
    219
    Par défaut
    Ok je comprends. Donc si je veux un tableau de pointeurs sur des données elle-même stockées de manière contiguës, alors je dois créer un allocateur particulier et donner ça à manger à un std::vector<T*> alors c'est ça ? Je commence à mieux comprendre.

    Merci !

  12. #12
    Expert éminent
    Avatar de Pyramidev
    Homme Profil pro
    Développeur
    Inscrit en
    Avril 2016
    Messages
    1 470
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Haute Garonne (Midi Pyrénées)

    Informations professionnelles :
    Activité : Développeur

    Informations forums :
    Inscription : Avril 2016
    Messages : 1 470
    Points : 6 108
    Points
    6 108
    Par défaut
    Salut,

    À la place de std::vector<T*>, si aucun pointeur ne peut être nul, on peut utiliser std::vector<std::reference_wrapper<T>>.
    std::reference_wrapper modélise une référence réassignable. Conceptuellement, c'est l'équivalent d'un pointeur jamais nul.

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

Discussions similaires

  1. Réponses: 14
    Dernier message: 25/10/2007, 15h00
  2. Exceptions, quand les utiliser
    Par JuTs dans le forum Général Dotnet
    Réponses: 6
    Dernier message: 13/05/2007, 15h27
  3. [Smarty] Utilité ? Quand les utiliser ?
    Par Xunil dans le forum Bibliothèques et frameworks
    Réponses: 25
    Dernier message: 28/11/2006, 17h08
  4. fonctions et classes... quand les utiliser ?
    Par fastmanu dans le forum Langage
    Réponses: 6
    Dernier message: 03/04/2006, 00h39

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