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 :

(non) utilisation du pointeur this dans une classe héritée générique


Sujet :

Langage C++

  1. #1
    Membre averti
    Profil pro
    Inscrit en
    Novembre 2008
    Messages
    24
    Détails du profil
    Informations personnelles :
    Âge : 36
    Localisation : France

    Informations forums :
    Inscription : Novembre 2008
    Messages : 24
    Par défaut (non) utilisation du pointeur this dans une classe héritée générique
    Bonjour,

    J'ai l'habitude de ne pas utiliser le pointeur this pour accéder aux membres de ma classe. Je suis simplement une convention de nomage qui dit que tout nom d'un membre non public doit commencer par un "_".
    Ainsi, il ne peut y avoir de confusion entre membre et variable locale comme illustré ci-dessous :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
     
    class MaClasse {
    public:
        ...
        setAttribut(int attribut) {
            _attribut = attribut;
        }
     
    protected:
        int _attribut;
    };
    N.B : Je sépare bien sûr déclaration/définition dans leur fichier respectif, j'ai tout mit ensemble afin de simplifier.

    Accès aux membres hérités depuis une classe dérivée :
    Cela fonctionne également lorsque il s'agit d'accéder aux membres protégés d'une classe dérivée qui ont été hérités d'une classe de base :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
     
    class Base {
    protected:
        int _attribut;
    };
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
     
    class Derivee : public Base {
    public:
        Derivee(int attribut) {
            _attribut = attribut;
        }
    };
    Avec les classes génériques...
    Mais en voulant m'exercer avec les classes génériques (utilisation de templates), je me suis confronté à un problème.
    Lorsque une classe générique hérite d'une autre classe générique, l'accès aux membres protégés de la classe de base depuis la classe héritée sans l'utilisation du pointeur this ne fonctionne pas :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
     
    template<typename T>
    class BaseGenerique {
    protected:
        T _attribut;
    };
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
     
    template<typename T>
    class DeriveeGenerique : BaseGenerique<T> {
    public:
        DeriveeGenerique(T attribut) {
            _attribut = attribut;       // erreur : '_attribut' was not declared in this scope
            this->_attribut = attribut; // fonctionne
        }
    };
    Vous allez me dire "Mais pourquoi n'utilise-tu pas le pointeur this dans tous les cas ?". C'est vrai, mais je voulais simplement savoir d'où cette différence pouvais bien venir.

    Merci.

    EDIT : Je suis sous Mac OS X 10.6.4, j'utilise Xcode et GCC 4.2 pour la compilation.

  2. #2
    Membre Expert
    Avatar de Klaim
    Homme Profil pro
    Développeur de jeux vidéo
    Inscrit en
    Août 2004
    Messages
    1 717
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Développeur de jeux vidéo
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Août 2004
    Messages : 1 717
    Par défaut
    Attention avec ce genre de convention, _ en préfixe est réservé par le standard pour les implémentations de libraries standard (si je me souviens bien).

    Cela dit la plupart du temps ça ne gene pas mais attention tout de même.

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

    Informations professionnelles :
    Activité : aucun

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

    Je sais qu'il y a effectivement une raison bien précise pour que l'utilisation de this soit nécessaire, mais je n'arrive plus à me souvenir laquelle (il me semble cependant qu'elle a déjà été donnée sur le forum).

    Cependant:

    La philosophie principale de la programmation orientée objets est de s'intéresser aux services que peut rendre une classe, aux messages qu'elle peut émettre ou recevoir, plutôt qu'aux données qui lui permettent de rendre ces services.

    Il existe d'ailleurs la loi demeter nous dit, pour faire simple, que si un objet de type A utilise manipule un objet de type B et que cet objet de type B manipule lui-même un objet de type C, notre objet de type A n'a aucune raison d'avoir connaissance de l'existence du type C.

    Il est donc souvent préférable de déterminer les différents services que l'on peut attendre des différentes classes et de laisser ceux-ci manipuler les différents membre de la classe sans forcément fournir un accesseur ("getter") sur ces membres.

    Le seul cas dans lequel nous fournirions effectivement un accesseur serait le cas où la donnée en question correspond effectivement à la réponse que l'on peut attendre d'un service donné:

    Une classe personne, par exemple, doit pouvoir répondre à des messages comme "quel est ton nom?" ou "quel est ton prénom", et il est donc compréhensible de fournir un accesseur sur le nom et un autre sur le prénom.

    Mais autrement, il n'y a souvent aucune raison de vouloir récupérer un membre de classe "tel quel":

    On se doute bien qu'une classe "voiture" disposera d'un membre "réservoir", qu'il s'agira de le remplir et de l'interroger régulièrement pour évaluer l'autonomie de la voiture, mais il n'y a strictement aucune raison pour permettre à l'utilisateur de... récupérer le dit réservoir en brut: il sera manipulé au travers de services ou états fournis par la voiture comme "état reservoir".

    Ce qui est vrai pour les accesseurs l'est d'autant plus pour les mutateurs ("setters"): Il y a peu de chance que quelqu'un puisse vouloir... changer le réservoir d'un voiture, et, de toutes manières, il veillera généralement à garder un réservoir de même type (même forme, même capacité, même position du point de sortie vers le moteur, ...).

    Par contre, certains messages reçus par la voiture peuvent modifier l'état du réservoir: le service "rajouter carburant (xxx litres)" ou l'état "moteur tournant" vont agir sur le réservoir en faisant varier la quantité de carburant qu'il contient.

    Et même dans le cas où un accesseur s'avère intéressant, il n'est pas forcément dit que le mutateur le soit: si tu change le nom ou le prénom d'une personne, tu... change carrément de personne, et tu devrais donc en créer une autre.

    Il existe d'ailleurs un principe nommé RAII Ressource Acquizition Is Initialization qui te conseille de faire en sorte que toute variable créée soit directement initialisée avec des valeurs cohérentes, de manière à être directement utilisable.

    C'est la raison d'être du constructeur, et tu peux, bien évidemment, donner autant de constructeurs que tu veux, avec éventuellement des valeurs par défaut.

    Mais, dans le constructeur, il est préférable la liste d'initialisation

    Enfin, il n'y a, a priori, aucune raison d'attendre d'être au niveau d'une classe dérivée pour définir un service qui devra de toutes manières être fourni par la classe "parent" et par l'ensemble des classes dérivées.

    Pour résumer, on pourrait dire qu'il faut utiliser "rarement les accesseurs et les mutateurs encore plus rarement", mais, si on les utilise, il vaut mieux les définir directement dans la classe qui dispose du membre qu'ils doivent manipuler:
    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
     
    Class Base
    {
        public:
            Type const & getX() const{return _x;}
            void setX(Type const & newx){_x=newx;}
            virtual ~Base(){}
        private:
            Type _x;
    };
    class Derivee : public Base
    {
        /* ce qui est spécifique à Derivee */
    };
    int main()
        /* Aucun problème pour la classe dérivée : on peut utiliser 
         * getX et setX
         */
        Derivee d;
        d.getX(); //aucun problème
        d.setX(/*...*/); // aucun problème
        return 0;
    }
    Avec les listes d'initialisation, cela ferait:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    class Base
    {
        public:
            Base(Type const & t):_t(t){}
            virtual ~Base(){}
        private:
            Type _t;
    };
    class Derivee : public Base
    {
        public:
            Derivee(Type t):Base(t){}
    };
    Et, avec une classe template et la liste d'initialisation, cela ferait
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    template <class T>
    class Base
    {
        public:
            Base(T const & t):_t(t){}
        private:
            T _t;
    };
    class Derivee : public Base<std::string>
    {
        public:
            Derivee(std::string const & s):Base<std::string>(s){}
    };
    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
    Expert confirmé
    Avatar de Luc Hermitte
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Août 2003
    Messages
    5 296
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Haute Garonne (Midi Pyrénées)

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : Aéronautique - Marine - Espace - Armement

    Informations forums :
    Inscription : Août 2003
    Messages : 5 296
    Par défaut
    Tiens. Ce n'est pas dans "notre" FAQ.
    -> http://www.comeaucomputing.com/techt.../#whythisarrow

    Sinon, +1 aux remarques de mes deux collègues.
    Blog|FAQ C++|FAQ fclc++|FAQ Comeau|FAQ C++lite|FAQ BS|Bons livres sur le C++
    Les MP ne sont pas une hotline. Je ne réponds à aucune question technique par le biais de ce média. Et de toutes façons, ma BAL sur dvpz est pleine...

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

Discussions similaires

  1. [Prototype] Utilisation d'un timer dans une classe
    Par vanitom dans le forum Bibliothèques & Frameworks
    Réponses: 3
    Dernier message: 11/05/2009, 15h17
  2. [Prototype] Portée de this dans une classe
    Par grunk dans le forum Bibliothèques & Frameworks
    Réponses: 2
    Dernier message: 07/04/2009, 14h58
  3. Prob: Utilisation d'un Movieclip dans une classe, toujours même instanceutilisée
    Par scourchesne dans le forum ActionScript 1 & ActionScript 2
    Réponses: 0
    Dernier message: 08/11/2008, 01h14
  4. Utiliser des variables applications dans une classe
    Par soso78 dans le forum VB.NET
    Réponses: 1
    Dernier message: 13/10/2008, 22h21
  5. [debutant] Utilisation de l'operateur = dans une classe
    Par Battosaiii dans le forum Débuter
    Réponses: 8
    Dernier message: 10/11/2005, 23h01

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