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 :

Reinitialiser une variable


Sujet :

C++

Vue hybride

Message précédent Message précédent   Message suivant Message suivant
  1. #1
    Membre confirmé
    Profil pro
    Inscrit en
    Mai 2005
    Messages
    66
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Mai 2005
    Messages : 66
    Par défaut Reinitialiser une variable
    Salut à tous,

    Je sèche sur un problème totalement stupide, qui démontre bien mon ignorance de ce langage pointu qu'est le C/C++

    Lorsque je déclare une variable membre d'un certain type, que je l'assigne, comment puis-je revenir à l'état initial de cette variable, et tout d'abord, que contient-elle ?

    Un exemple pour que cela soit plus concret

    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 Message 
    {
        void Message() {};
        void init() {m_text = "blah"};
        std::string getText() {return m_text};    
     
        private :
        std::string m_text;
    }
     
    class User
    {
        void User();
        void checkMsg() {m_Message.init()};
     
        private :
        Message m_Message;
    }
    Vous l'aurez compris, cet exemple est bidon, c'est pour illustrer ce que je souhaite faire.

    Si j'instancie un nouvel utilisateur, je ne sais pas bien ce que vaut la variable m_Message, de même, après avoir appelé la fonction checkMsg, je ne sais pas réinitialiser la variable m_Message à son état initial, c'est à dire retourner dans l'état où m_text ne vaut pas "blah" (d'ailleurs, il vaut quoi quand la méthode init n'a pas été appelée ?)

    Merci de votre aide,

    Guiz

  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,

    Le propre du paradigme orienté objet et de ne plus réfléchir en terme de données mises ensemble pour représenter une donnée plus complexe, mais bien de réfléchir en termes de services que l'on attend de l'objet.

    Un service attendu peut être, selon le cas, la réponse à une question que l'on pose, la réaction adaptée à un message qu'on lui transmet ou, pourquoi pas, l'émission d'un message vers un objet connu de l'objet auquel on s'adresse.

    Par exemple, en programmation séquentielle pure, une voiture serait vue sous l'angle de l'ensemble des objets moins "complexes" qui la composent: la carrosserie, le moteur, quatre roues, un volant, un réservoir d'essence, plusieurs sièges ou banquettes etc alors que, d'un point de vue OO, on s'intéressera au fait qu'elle peut démarrer, ralentir, tourner, nous répondre à des questions comme "quel est l'état de remplissage du réservoir" ou "de combien de places dispose-t-on" mais que ce dont la voiture est composé est finalement secondaire...

    Parmi les services qu'un objet peut rendre, il y a deux types particuliers qui peuvent agir directement sur une donnée membre: les accesseurs et les mutateurs ( "getter" et "setter", en anglais)

    Le rôle d'un accesseur est, simplement, de permettre à l'utilisateur de disposer... de l'information demandée (comme c'est le cas de ta fonction "getText") mais... en lecture seule.

    Celui d'un mutateur est, tout aussi simplement, de permettre à l'utilisateur de modifier une information particulière, mais, en toute logique, uniquement sur... des objets non constants.

    On passe, pour ce faire, par la transmission au mutateur d'un argument du type concerné.

    Quelque part, ta fonction "init" est fort proche de ce que pourrait être un mutateur, même si elle pourrait être adaptée et renommée.

    En effet, le service que tu attend de init n'est pas... d'initialiser ton objet...:

    Il existe en effet un principe nommé RAII (Ressource Acquisition Is Initialization, ou, si tu préfère la langue de voltaire, L'acquisition des ressources sert d'initialisation) qui conseille de veiller à ce que l'objet soit utilisable dés qu'il est construit...

    L'initialisation devrait donc survenir... dans le constructeur.

    Par contre, le service que tu attend de init serait, de manière beaucoup plus générale de... pouvoir modifier la valeur du membre m_text...

    L'idée est donc de placer un mutateur sur m_text, que l'on pourrait nommer "setText" et auquel nous passerions... un paramètre de type std::string correspondant...

    De cette manière, lorsque tu veux modifier le texte, que ce soit pour le "réinitialiser" ou, simplement pour le changer, tu n'aura plus qu'à... appeler cette fonction setText.

    Elle pourrait prendre la forme de
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    class Message
    {
        public:
           void setText(std::string const & m){m_text = m;}
           /* le reste de la classe */
    };
    Je voudrais par contre revenir sur ton accesseur "getText", car je me dois de réagir dessus:

    Il est généralement conseillé d'éviter les copies d'objet autant que faire se peut...

    En effet, lorsqu'il y a copie, il y a, "en toile de fond", tout un mécanisme qui peut être lent et couteux en terme de mémoire qui est mis en œuvre.

    C'est pourquoi, il est conseillé chaque fois que faire se peut de passer ou de renvoyer tes différents objets sous la forme de référence.

    En effet, la référence agit strictement comme un alias de l'objet transmis / renvoyé, ce qui évite la copie.

    Par contre, il faut prendre la précaution d'empêcher que l'objet ne soit modifié (au travers de son alias) s'il ne doit pas l'être.

    il serait en effet dommage (et potentiellement dangereux) de permettre un
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    Message msg;
    msg.getText()="salut";
    qui aurait pour résultat... de modifier notre message msg...

    Dans ce cas, il faut donc transmettre /renvoyer cet objet sous forme d' une référence constante.

    L'avantage direct que nous en tirerons sera de pouvoir faire en sorte que getMessage s'engage à ne pas modifier l'objet au départ duquel elle est invoquée en la déclarant constante sous la forme de
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    class Message
    {
        public:
            std::string const & /* (1 ) */ getMessage() const /* ( 2 ) */
            {
                return m_text;
            }
            /* ... */
    };
    (NOTA : Le const en (1) indique que la chaine renvoyé ne peut pas être modifiée et le const en (2) indique que getText s'engage à ne pas modifier l'objet courent)
    Nous pourrions donc envisager une fonction proche de
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    void doSomething(Message const & m) /* m n'a pas vocation à être 
                                         * modifié 
                                         */
    {
        m.getText(); // OK
        m.setText("bonjour"); // KO... on a dit que m ne peut pas etre modifié...
    }
    Enfin, je ne peux parler des accesseurs et mutateurs sans me fendre d'un conseil important:

    S'ils sont nécessaires, voire, indispensables dans certains cas, il faut les utiliser avec prudence car de nombreuses variables membres n'en on purement et simplement pas besoin:

    Il ne sert à rien de placer un accesseur, ou pire, un mutateur, sur une variable qui n'est utilisée que de manière strictement interne à l'objet en cours


    PS: C/C++ n'existe pas et est sans doute *le* terme honnis par excellence...

    En effet, même si C++ hérite de C, ce terme entretient la confusion sur le fait qu'il s'agit de deux langages totalement différents, avec, dans le meilleur des cas, la tentation d'utiliser (ce qui est fortement déconseillé) en C++ des méthodes propres à C et, dans le pire des cas d'utiliser en C des méthodes propres à C++ (ce qui est interdit)...
    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 confirmé
    Profil pro
    Inscrit en
    Mai 2005
    Messages
    66
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Mai 2005
    Messages : 66
    Par défaut
    Merci à toi koala01 pour cette précision très instructive sur la façon d'accéder aux variables membres d'une classe.

    Bien que je trouve pléthores de bon conseils dans la grande réponse que tu m'as faite, je ne trouve pas la réponse à ma question initiale, qui n'était pas de savoir comment mettre une variable membre à un état, mais qui était de savoir ce que valait une variable membre de type objet avant son attribution.

    En gros, quand on fait

    std::string text;

    Que vaut text ? NULL ?

    Et une fois qu'on a fait

    text = "blah";

    Comment le remettre dans sa valeur initiale ? (text = NULL; ?)

    J'ai essayé avec le NULL, ça ne semble pas fonctionner, donc c'est pour cela que je m'interroge.

    Merci,

    Guiz

  4. #4
    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
    Citation Envoyé par ZeGuizmo Voir le message
    Que vaut text ? NULL ?
    Surement pas... NULL est une valeur dédiée à la gestion des pointeurs...

    text n'est pas un pointeur

    La classe std::string respecte le RAII et dispose d'un constructeur par défaut (ne prenant aucun argument) qui initialise la chaine sous la forme... d'une chaine vide (équivalent à "")...

    C'est ce constructeur de std::string qui est appelé automatiquement par le constructeur de ta classe Message lorsque tu en crées une instance
    Et une fois qu'on a fait

    text = "blah";

    Comment le remettre dans sa valeur initiale ? (text = NULL; ?)
    Tu auras compris que ce n'est pas NULL qu'il faut lui donner comme valeur, mais ... ""
    J'ai essayé avec le NULL, ça ne semble pas fonctionner, donc c'est pour cela que je m'interroge.
    Et pour cause...

    Mais les explications et la réponses viennent d'être donné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

  5. #5
    Rédacteur

    Avatar de Davidbrcz
    Homme Profil pro
    Ing Supaéro - Doctorant ONERA
    Inscrit en
    Juin 2006
    Messages
    2 307
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 33
    Localisation : Suisse

    Informations professionnelles :
    Activité : Ing Supaéro - Doctorant ONERA

    Informations forums :
    Inscription : Juin 2006
    Messages : 2 307
    Par défaut
    Il faut cependant comprendre que ta chaîne de caractère possède la valeur "" par défaut car son constructeur par défaut lui donne cette valeur. Mais dans le cadre de type natif (int, double, ...) les valeurs sont indéterminés.
    Exemple
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    struct foo
    {
    int i;
    };
     
    int main()
    {
    foo f;
    std::cout<<f.i<<std::endl; //peut afficher n'importe quoi (chez moi 1069653584)
    return 0;
    }
    "Never use brute force in fighting an exponential." (Andrei Alexandrescu)

    Mes articles dont Conseils divers sur le C++
    Une très bonne doc sur le C++ (en) Why linux is better (fr)

  6. #6
    Membre confirmé
    Profil pro
    Inscrit en
    Mai 2005
    Messages
    66
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Mai 2005
    Messages : 66
    Par défaut
    Merci pour vos deux réponses, elles apportent toutes deux des réponses à la question que je me pose.

    J'allais justement réagir sur les types natifs

    Si j'ai bien compris, pour résumer, les types natifs prennent des valeurs de leur type mais indeterminées, donc il n'y a pas lieu d'effectuer une réinitialisation. Et pour les objets bénéficiant d'un constructeur, le simple fait de faire

    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
    class foo 
    {
        foo() {};
    }
     
    class bar 
    {
        bar() {};
        private :
        foo privateFoo;
    }
     
    int main()
    {
        bar b;
    }
    Va appeler les constructeurs par défaut de bar et de foo, donc ici, pour réinitialiser la variable b si je la modifie, je dois explicitement appeler le destructeur et la ré-instancier en faisant un truc du genre : b = new bar(); ?
    Et le destructeur de bar, dois-je le déclarer explicitement pour que lui aussi, il appelle le destructeur de foo ?

    Merci de votre patience

    Guiz

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

Discussions similaires

  1. [XL-2007] Juste pour ma gouverne : Comment reinitialiser une variable
    Par ALEX80800 dans le forum Macros et VBA Excel
    Réponses: 6
    Dernier message: 24/11/2013, 16h13
  2. Reinitialiser une variable static
    Par anat1212 dans le forum C
    Réponses: 0
    Dernier message: 29/11/2009, 21h38
  3. [BES] Création d'une variable d'environnement
    Par NGI80 dans le forum Autres
    Réponses: 2
    Dernier message: 17/10/2002, 07h31
  4. Désigner une variable avec une variable?
    Par littleman dans le forum Paradox
    Réponses: 4
    Dernier message: 12/08/2002, 11h21
  5. Réponses: 4
    Dernier message: 05/06/2002, 14h35

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