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 :

Vider le tableau générique(Template)


Sujet :

C++

Vue hybride

Message précédent Message précédent   Message suivant Message suivant
  1. #1
    Membre averti
    Femme Profil pro
    Étudiant
    Inscrit en
    Octobre 2022
    Messages
    17
    Détails du profil
    Informations personnelles :
    Sexe : Femme
    Localisation : Canada

    Informations professionnelles :
    Activité : Étudiant
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Octobre 2022
    Messages : 17
    Par défaut Vider le tableau générique(Template)
    Bonjour,

    Comment remplir la methode vider()? Quelqu'un a une idée?


    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
    template <class T>
    class TableauDynamique
    {
    public:
     
        TableauDam(int capacite_initiale = 5);
        TableauDam(const TableauDynamique&);
     
        void           vider();
     
    private:
        T*            elements;
        int            capacity;
        int            nbElements;
    };
     
     
    template <class T>
    void TableauDam<T>::vider()
    {
         ????
    }

  2. #2
    Expert confirmé
    Homme Profil pro
    Ingénieur développement matériel électronique
    Inscrit en
    Décembre 2015
    Messages
    1 599
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 62
    Localisation : France, Bouches du Rhône (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Ingénieur développement matériel électronique
    Secteur : High Tech - Électronique et micro-électronique

    Informations forums :
    Inscription : Décembre 2015
    Messages : 1 599
    Par défaut
    Bonjour,

    vider(), c'est l'inverse de remplir().
    Si on suppose que elements est un tableau alloué dynamiquement - très mauvaise idée de vouloir gérer cela - il faut tout détruire.
    Par exemple:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    template<class T>
    void TableauDam<T>::vider() {
        for ( size_t  i = nbElements ; i > 0 ; --i )
            delete elements[i-1];  // destruction des éléments dans l'ordre inverse de leur création
        nbElements = 0;
        // pour vraiment tout vider, on peut aussi ajouter:
        delete elements;
        capacity = 0;
        elements = nullptr;
    }
    On sait alors quoi faire dans le destructeur de TableauDam.

  3. #3
    Membre averti
    Femme Profil pro
    Étudiant
    Inscrit en
    Octobre 2022
    Messages
    17
    Détails du profil
    Informations personnelles :
    Sexe : Femme
    Localisation : Canada

    Informations professionnelles :
    Activité : Étudiant
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Octobre 2022
    Messages : 17
    Par défaut
    Merci beaucoup ))

    Est-ce que je peux garder cela ou bien faut-il que les deux soient presents?

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    for ( size_t  i = nbElements ; i > 0 ; --i )
        delete elements;
        capacity = 0;
        elements = nullptr;

  4. #4
    Expert confirmé
    Homme Profil pro
    Ingénieur développement matériel électronique
    Inscrit en
    Décembre 2015
    Messages
    1 599
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 62
    Localisation : France, Bouches du Rhône (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Ingénieur développement matériel électronique
    Secteur : High Tech - Électronique et micro-électronique

    Informations forums :
    Inscription : Décembre 2015
    Messages : 1 599
    Par défaut
    Citation Envoyé par Nigush Voir le message
    Merci bcp))

    Est-ce que je peux garder cela ou bien faut-il que les deux soient presents?
    C'est à toi de voir. Je ne sais ce que la fonction vider() est sensée faire.

  5. #5
    Membre Expert
    Profil pro
    Inscrit en
    Juillet 2006
    Messages
    1 466
    Détails du profil
    Informations personnelles :
    Localisation : France, Paris (Île de France)

    Informations forums :
    Inscription : Juillet 2006
    Messages : 1 466
    Par défaut
    Vu que tu manipules un tableau dynamique, pourquoi n'utilises tu pas std::vector ?

    Et au passage, tu as une grosse fuite de mémoire.

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

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 633
    Par défaut
    Salut,
    Citation Envoyé par Nigush Voir le message
    Merci beaucoup ))

    Est-ce que je peux garder cela ou bien faut-il que les deux soient presents?

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    for ( size_t  i = nbElements ; i > 0 ; --i )
        delete elements;
        capacity = 0;
        elements = nullptr;
    Bon, on va déjà partir du principe que tu es en phase de découverte, et que tu souhaites développer par toi même une fonctionnalité comme les tableaux dont la taille est gérée à l'exécution afin de t'exercer.

    Autrement, tu as très largement intérêt à utiliser, ainsi que l'a fait remarquer Deedolith, la classe std::vector qui fera tout cela sans doute bien mieux que tu ne sauras le faire avant longtemps

    Le gros problème de la discipline dite du "développement informatique" est qu'elle ne nous permet pas de nous contenter d'une simple vision "à court terme" des solutions à mettre en oeuvre.

    Bien sur, cette vision à court terme est absolument indispensable, ne serait-ce que pour pouvoir déterminer dans quelle situation bien particulière nous nous trouvons à un instant bien particulier du programme ainsi ce qui doit être fait et dans quel ordre cela doit être fait pour obtenir un résultat bien déterminé qui sera à la fois prédictible et reproductible:
    - prédictible: dont on peut déterminer "par avance" le résultat qui sera obtenu dans une situation donnée rien qu'en observant les données qui seront manipulées
    - reproductible: qui offre la garantie que nous obtiendrons deux fois le même résultat si nous lançons deux fois le même traitement dans les mêmes circonstances avec les mêmes données.

    Seulement, on a très (trop?) facilement tendance à oublier que, quelle que soit la fonctionnalité que l'on s'apprête à développer, elle sera systématiquement appelée à être utilisée dans "un flux d'exécution", avec "un certain nombre" d'autres fonctionnalités qui auront été appelée "avant" et "un certain nombre" de fonctionnalités qui risquent d'être utilisées "après".

    Si l'on n'a clairement plus la capacité de changer quoi que ce soit à ce qui a été effectué avant que notre fonctionnalité ne soit utilisée, il faut par contre systématiquement arriver à prendre suffisamment de recul par rapport au problème que pour arriver à ... se faire une idée "aussi précise que possible" de ce qui va se passer après, une fois que nous aurons obtenu le résultat que l'on attendait de la part de la fonctionnalité que l'on s'apprête à développer.

    Car il n'est en effet pas rare que ce qui "va se passer après" ait une influence "très importante" sur "le mode de fonctionnement" de la fonctionnalité que l'on veut développer.

    Dans le cas présent, tu te limites à ta "vision à court terme" en te disant que "je veux vider le contenu de mon tableau". Et tu as certainement raison de le faire, parce que c'est "le problème auquel tu veux apporter une solution, ici et maintenant".

    Seulement, si tu veux avoir une chance d'apporter "la meilleure solution possible" à ce problème, il faut absolument penser à te poser la question de "et que va-t-il se passer après que l'on aura vidé le contenu du tableau".

    Or, le simple fait de se poser la question devrait (enfin, quand tu auras sans doute pris un peu l'habitude de te demander ce qui va se passer après) t'amener "assez facilement" à la conclusion que deux situations a priori totalement différentes peuvent se présenter:

    Dans la première, le tableau cesse "purement et simplement" d'exister, et on va perdre toute trace de son existence. Au point que l'on pourrait, pour ainsi dire, croire qu'il n'a même jamais existé.

    Dans la deuxième, le tableau continue à exister, et on peut donc continuer à l'utiliser a priori "normalement", ou, à tout le moins, sans avoir à s'inquiéter qu'il vient en réalité d'être vidé de son contenu.

    Si le tableau est destiné à cesser d'exister après avoir été vidé, il faut donc s'assurer qu'il fasse "correctement le ménage" en libérant toutes les ressources qu'il utilise "avant de partir".

    Par contre, si le tableau continue d'exister et qu'il reste donc utilisable par la suite, alors, nous avons encore deux autres possibilités à prendre en compte:

    Soit, on peut s'attendre à ce qu'il ait -- effectivement -- libéré toutes les ressources qu'il utilisait, soit on estime qu'il est "cohérent" que le tableau garde ces ressources de manière à ne pas devoir les redemander lorsqu'il en aura besoin.

    Les deux situations sont plausibles, les deux ont des avantages, et les deux ont des inconvénients.

    En effet, le fait de libérer les ressources "devenues inutiles" permettra sans doute de "limiter un peu" la quantité de mémoire globale utilisée par l'application, mais, par contre, cela impliquera qu'il faille faire "quelque chose de plus" avant de pouvoir recommencer y placer de nouveaux éléments.

    A l'inverse, le fait de ne pas libérer les ressources "parce qu'elle pourront être réutilisées" facilitera sans doute le fait de recommencer à y placer des éléments, mais, par contre, impliquera "forcément" le fait de ne pas rendre les ressources utilisables pour "autre chose".

    Et là, on se rend compte que le développeur peut -- s'il le souhaite -- choisir "arbitrairement" ce qui peut se passer "après que le tableau ait été vidé", mais qu'il serait sans doute "beaucoup plus utile" de ... laisser l'utilisateur du tableau décider de ce qui va se passer par la suite.

    J'en profite d'ailleurs pour t'apprendre que, dans l'idéal, tu ne dois te considérer comme le développeur d'une fonctionnalité que le temps nécessaire à en écrire le code (et à t'assurer qu'elle fonctionne correctement) et que, une fois que c'est fait, tu devrais -- dans l'idéal -- te considérer comme "un utilisateur normal" de la fonctionnalité, qui ne sait absolument rien de plus qu'un autre quant à la manière dont la fonctionnalité arrive à fournir le résultat attendu.

    En outre, il faut savoir qu'il y a un ensemble de principes de conception appelé SOLID pour
    • S comme SRP ou Single Responsability Principle (principe de la responsabilité unique): chaque type de donnée, chaque donnée, chaque fonction ne devrait avoir qu'une et une seule responsabilité, pour avoir un chance d'être en mesure de faire les choses correctement
    • O comme OCP ou Open Close Principle (principe "ouvert fermé"): ton code devrait être "ouvert aux évolution" (comprends: permettre les évolutions futurs) mais "fermé au modifications" (comprend: un code vérifié et validé ne devrait pas être modifié pour pouvoir intégrer une évolution)
    • L comme LSP ou Liskov Substitution Principle (principe de substitution de Liskov): en résumé, il s'agit du principe qui s'occupe de toute la problématique liée à l'héritage et au polymorphisme d'inclusion. Il justifierait toute une discussion à lu tout seul
    • I comme ISP ou Interface Segregation Principle (principe de ségrégation des interfaces): Vos interfaces ne doivent exposer que "juste assez de possibilités" que pour permettre une utilisation correcte et cohérente de vos fonctionnalités.
    • D comme DIP ou Dependancies Inversion Principle (principe d'inversion des dépendances): une abstraction ne doit pas dépendre de détail d'implémentation, ce sont les détails d'implémentation qui doivent dépendre des abstractions.


    Je cite les cinq ici pour mémoire, histoire de t'inciter à te renseigner au sujet de chacun d'eux . Par contre, il me semble particulièrement utile d'attirer ton attention sur le SRP dans l'immédiat.

    Car, si on prend le code (tout à fait correct au demeurant) proposé par Daflab, et que je me pemets de commenter, on a
    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>
    void TableauDam<T>::vider() {
       /* on détruit les éléments contenu dans le tableau (*) */
        for ( size_t  i = nbElements ; i > 0 ; --i )
            delete elements[i-1];  // destruction des éléments dans l'ordre inverse de leur création
       /* met le nombre d'éléments du tableau à jour (**) */
        nbElements = 0;
        // pour vraiment tout vider, on peut aussi ajouter:
        /* on force le tableau à libérer toutes les ressources dont il a la charge (***) */
        delete elements;
        capacity = 0;
        elements = nullptr;
    }
    on se rend compte que cette fonction vider prend à elle seule trois responsabilités, à savoir
    1. (*) la responsabilité de détruire les éléments contenus par le tableau
    2. (**) la responsabilité de mettre le nombre d'éléments contenu par le tableau à jour
    3. (***) la responsabilité de libérer les ressources dont le tableau a la charge

    Autrement dit, même en considérant qu'il est "logique" de supprimer un (ou plusieurs) élément(s) du tableau et de mettre le nombre d'éléments restants à jour "en même temps", ce code présente malgré tout encore deux responsabilités majeures, à savoir : supprimer les éléments du tableau et libérer les ressources occupées par le tableau.

    Et cela, en vertu du SRP, cela représente ... une responsabilité majeure de trop

    Donc, si on veut respecter le SRP, il faudrait -- au minimum -- s'assurer d'avoir ... deux fonctions prenant chacune une (et une seule) de ces responsabilité majeure, ce qui nous donnerait un code proche de
    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
     
    template <class T>
    class TableauDynamique{
    public:
         TableauDynamique(int capacite_initiale = 5);
        TableauDynamique(const TableauDynamique&);
        ~TableauDynamique(); // parce qu'il en va de ta responsabilité de développeur de t'assurer
                       // que la destruction du tableau fera correctement le ménage
        void vider(); // ne fera effectivement que supprimer l'ensemble des éléments du tableau
    private:
        void libererRessources(); // ne fera effectivement que libérer les ressources
                                  // NOTA: placée dans l'accessibilité privée car l'utilisateur
                                 // n'est pas censé avoir "directement accès" à cette fonctionnalité
     
        T*            elements;
        int            capacity;
        int            nbElements;
    };
     
    template<class T>
    TableauDynamique::~TableauDynamique(){
        /* nous devons nous assurer de vider le tableu
         * et de libérer les ressrources qu'il utilise...
         * pourquoi ne pas, tout simplement, appeler les 
         * fonctions équivalentes?
         */
       vider();
       libererRessoures();
    }
    template<class T>
    void TableauDynamique<T>::vider() {
        for ( size_t  i = nbElements ; i > 0 ; --i )
            delete elements[i-1];  // destruction des éléments dans l'ordre inverse de leur création
        nbElements = 0;
    }
    template<class T>
    void TableauDynamique<T>::libererRessources() {
        delete[] elements;
        capacity = 0;
    }
    Et voilà qui nous permet de respecter le SRP tout en garantissant que le ménage sera correctement fait si le tableau cesse d'exister après avoir été vidé.

    Le code tel qu'il se présente permet en outre à l'utilisateur de vider le tableau tout en gardant les ressources "qu'il serait dommage de devoir redemander pour la seule raison qu'on voulait vider le tableau".
    Si bien que, en fait, tout ce qu'il nous reste éventuellement à gérer comme situation (pour autant que l'on estime qu'il y ait du sens à permettre à l'utilisateur de le faire), c'est de permettre à l'utilisateur de demander explicitement que les ressources utilisées par le tableau soient libérées après que le tableau ait été vidé.

    Nous pourrions donc modifier une fois encore le code pour lui donner la forme finale de
    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
    template <class T>
    class TableauDynamique{
    public:
         TableauDynamique(int capacite_initiale = 5);
        TableauDynamique(const TableauDynamique&);
        ~TableauDynamique(); // parce qu'il en va de ta responsabilité de développeur de t'assurer
                       // que la destruction du tableau fera correctement le ménage
        void vider(); // ne fera effectivement que supprimer l'ensemble des éléments du tableau
        void viderEtLibererRessources();
    private:
        void libererRessources(); // ne fera effectivement que libérer les ressources
                                  // NOTA: placée dans l'accessibilité privée car l'utilisateur
                                 // n'est pas censé avoir "directement accès" à cette fonctionnalité
     
        T*            elements;
        int            capacity;
        int            nbElements;
    };
     
    template<class T>
    TableauDynamique::~TableauDynamique(){
        /* nous devons nous assurer de vider le tableu
         * et de libérer les ressrources qu'il utilise...
         * pourquoi ne pas, tout simplement, appeler les 
         * fonctions équivalentes?
         */
       vider();
       libererRessoures();
    }
    template<class T>
    void TableauDynamique<T>::vider() {
        for ( size_t  i = nbElements ; i > 0 ; --i )
            delete elements[i-1];  // destruction des éléments dans l'ordre inverse de leur création
        nbElements = 0;
    }
    template<class T>
    void TableauDynamique<T>::libererRessources() {
        delete[] elements;
        capacity = 0;
    }
     
    template<class T>
    void TableauDynamique<T>::viderEtLibererRessources() {
        /* autant appeler les fonctions, c'est plus facile :D */
        vider();    // on vide
        libererRessources(); // on libere les ressources
    }
    grâce auquel tu arrives à n'imposer à l'utilisateur que "le stricte minimum" permettant de t'assurer qu'il utilisera correctement ton tableau. Tu en auras d'ailleurs profité sans même t'en rendre compte pour suivre un conseil particulièrement avisé donné par Scott Meyers, qui préconise de
    Rendre les interfaces faciles à utiliser correctement et difficiles à utiliser de manière incorrecte
    En conclusion:

    Les gens ont souvent tendance à croire que l'informatique (en générale) ou le développement informatique (en particulier), c'est quelque chose de facile, qu'il suffit de taper sur le clavier pour obtenir "comme par magie" ce que l'on veut, et que, si on n'y arrive pas, c'est forcément à cause de l'ordinateur.

    Ce n'est absolument pas vrai. Et le pire, c'est que de nombre de tutos et de cours tendent à perpétuer le mensonge.

    J'ai voulu "briser le mythe" par cette intervention, entre autre en insistant sur l'impérieuse nécessité d'avoir "une certaine vision à long terme" et qui m'a "naturellement" amené à parler d'éléments que la vision à court terme aurait eu tendance à cacher.
    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

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

Discussions similaires

  1. [débutant]Vider un Tableau
    Par bugmenot dans le forum Collection et Stream
    Réponses: 3
    Dernier message: 01/03/2007, 18h20
  2. vider un tableau de caractères
    Par cybersam dans le forum C
    Réponses: 20
    Dernier message: 23/05/2006, 21h35
  3. [Tableaux] Vider un tableau
    Par christel1982 dans le forum Langage
    Réponses: 11
    Dernier message: 22/03/2006, 11h08
  4. Comment vider un tableau de char ?
    Par Battosaiii dans le forum C
    Réponses: 5
    Dernier message: 18/03/2006, 17h42
  5. [Programmation générique] template - iterator_traits
    Par Paul Atreide dans le forum Langage
    Réponses: 2
    Dernier message: 14/03/2005, 23h09

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