Partage
  • Partager sur Facebook
  • Partager sur Twitter

Classe template, la valeur fictive est un pointeur d'objet

Comment "avoir l'objet" en lui même?

Sujet résolu
    1 octobre 2007 à 13:20:50

    Bonjour, je suis en train de m'entraîner à utiliser les classes template.
    Actuellement je travail sur une classe TableauPointeurs, qui a pour but de gérer un tableau de pointeur de classe, c'est-à-dire que cette classe contient un attribut membre qui est un tableau contenant des pointeurs de classe.

    Les codes:



    main.cpp :


    1. #include <iostream>
    2. #include <string>
    3. #include "headers/Tableau_pointeurs.h"
    4. #include "headers/Arme.h"
    5. #include "headers/Armure.h"
    6. using namespace std;
    7. int main()
    8. {
    9.         TableauPointeurs<Arme *> arme;
    10.                 int epee_base = arme.ajouterPointeur (new Arme ());
    11.                 int hache_de_nain = arme.ajouterPointeur (new Arme ("Hache de nain", 1.5));
    12.                 int epee_courte = arme.ajouterPointeur (new Arme ("Epee courte", 0.5));
    13.                 arme.supprimerPointeur (epee_courte);
    14.                 cout << "# Armes :" << endl;
    15.                 arme.afficherTableauPointeurs();
    16.         TableauPointeurs<Armure *> armure;
    17.                 int armure_base = armure.ajouterPointeur (new Armure ());
    18.                 int armure_de_nain = armure.ajouterPointeur (new Armure ("Armure de nain", 2));
    19.                 int armure_legere = armure.ajouterPointeur (new Armure ("Armure legere", 0.5));
    20.                 armure.supprimerPointeur (armure_legere);
    21.                 cout << "# Armures :" << endl;
    22.                 armure.afficherTableauPointeurs();
    23.         TableauPointeurs<Armure *> copie_armure (armure);
    24.         copie_armure.afficherTableauPointeurs();
    25.         return 0;
    26. }


    TableauPointeurs.h :


    1. #ifndef DEF_TABLEAU_POINTEURS
    2. #define DEF_TABLEAU_POINTEURS
    3.         #include <iostream>
    4.         #include <string>
    5.         template <class T>
    6.         class TableauPointeurs
    7.         {
    8.                 protected:
    9.                         T *v_tableau_pointeurs;
    10.                         int v_nombre_pointeurs;
    11.                         T* allouerTableauPointeurs (const int nombre_pointeurs) const
    12.                                 { return new T[nombre_pointeurs]; };
    13.                 public:
    14.                         TableauPointeurs();
    15.                         TableauPointeurs (const TableauPointeurs &tableau_pointeurs);
    16.                         ~TableauPointeurs();
    17.                         int ajouterPointeur (const T &pointeur);
    18.                         void supprimerPointeur (const int index);
    19.                         void viderTableauPointeurs();
    20.                         void afficherTableauPointeurs();
    21.                         int getNombrePointeurs() const
    22.                                 { return v_nombre_pointeurs; };
    23.                         T getPointeur (const int index) const
    24.                                 { return v_tableau_pointeurs[index]; };
    25.         };
    26.         template <class T>
    27.         TableauPointeurs<T>::TableauPointeurs()
    28.                                                 : v_nombre_pointeurs (0)
    29.         {
    30.                 v_tableau_pointeurs = allouerTableauPointeurs (v_nombre_pointeurs);
    31.         }
    32.         template <class T>
    33.         TableauPointeurs<T>::TableauPointeurs (const TableauPointeurs &tableau_pointeurs)
    34.                                                 : v_nombre_pointeurs (tableau_pointeurs.v_nombre_pointeurs)
    35.         {
    36.                 v_tableau_pointeurs = allouerTableauPointeurs (v_nombre_pointeurs);
    37.                 for (int i = 0; i < v_nombre_pointeurs; ++i)
    38.                 {
    39.                         //v_tableau_pointeurs[i] = new Armure (*(tableau_pointeurs.v_tableau_pointeurs[i]));
    40.                         v_tableau_pointeurs[i] = new T (*(tableau_pointeurs.v_tableau_pointeurs[i]));
    41.                 }
    42.         }
    43.         template <class T>
    44.         int TableauPointeurs<T>::ajouterPointeur (const T &pointeur)
    45.         {
    46.                 T *temp = allouerTableauPointeurs (v_nombre_pointeurs + 1);
    47.                 for (int i = 0; i < v_nombre_pointeurs; ++i)
    48.                         temp[i] = v_tableau_pointeurs[i];
    49.                 temp[v_nombre_pointeurs] = pointeur;
    50.                 delete[] v_tableau_pointeurs;
    51.                 v_tableau_pointeurs = temp;
    52.                 return (v_nombre_pointeurs++);
    53.         }
    54.         template <class T>
    55.         void TableauPointeurs<T>::supprimerPointeur (const int index)
    56.         {
    57.                 --v_nombre_pointeurs;
    58.                 T *temp = allouerTableauPointeurs (v_nombre_pointeurs);
    59.                 int i = 0;
    60.                 for (int j = 0; j < (v_nombre_pointeurs + 1); ++j)
    61.                 {
    62.                         if (j == index)
    63.                                 ++j;
    64.                         if (i < v_nombre_pointeurs)
    65.                                 temp[i] = v_tableau_pointeurs[j];
    66.                         ++i;
    67.                 }
    68.                 delete[] v_tableau_pointeurs;
    69.                 v_tableau_pointeurs = temp;
    70.         }
    71.         template <class T>
    72.         void TableauPointeurs<T>::viderTableauPointeurs()
    73.         {
    74.                 for (int i = 0; i < v_nombre_pointeurs; ++i)
    75.                         if (v_tableau_pointeurs[i] != NULL)
    76.                                 delete v_tableau_pointeurs[i];
    77.                 delete[] v_tableau_pointeurs;
    78.                 v_nombre_pointeurs = 0;
    79.                 v_tableau_pointeurs = allouerTableauPointeurs (v_nombre_pointeurs);
    80.         }
    81.         template <class T>
    82.         void TableauPointeurs<T>::afficherTableauPointeurs()
    83.         {
    84.                 for (int i = 0; i < v_nombre_pointeurs; ++i)
    85.                         std::cout << v_tableau_pointeurs[i] << std::endl;
    86.         }
    87.         template <class T>
    88.         TableauPointeurs<T>::~TableauPointeurs()
    89.         {
    90.                 for (int i = 0; i < v_nombre_pointeurs; ++i)
    91.                         if (v_tableau_pointeurs[i] != NULL)
    92.                                 delete v_tableau_pointeurs[i];
    93.                 delete[] v_tableau_pointeurs;
    94.         }
    95.         //#include "../Tableau_pointeurs.tpp"
    96. #endif


    Le problème :



    Le problème se situe dans le constructeur de copie.
    Plus précisément à la ligne 49:
    1. v_tableau_pointeurs[i] = new T (*(tableau_pointeurs.v_tableau_pointeurs[i]));


    L'erreur est donc une erreur de conversion:

    Citation : Erreur du compilateur :

    main.cpp:32: instantiated from here
    headers/Tableau_pointeurs.h:49: error: cannot convert `Armure' to `Armure*' in initialization



    Normal me direz-vous, puisque la valeur fictive T a dans ce cas la valeur Armure*, ce qui veut dire (si je ne me trompe pas, j'ai tendance à m'embouiller avec les pointeurs) qu'à la ligne 49 (cf.: ci-dessus) je crée un pointeur de pointeur puisque le new crée un pointeur vers Armure* qui est un pointeur.

    C'est pour celà que je voudrais savoir s'il est possible de récupérer l'objet de T et non pas un pointeur, j'ai bien essayé un *T mais sans succès j'ai juste le droit à une erreur.

    Conclusion :



    J'aimerais donc savoir s'il y as un moyen de faire ce que je veux faire ou si je m'y suis mal pris, dans quel cas est-ce que vous pouvez me donner quelques indications sur la méthode à suivre.
    Je précise que je ne tiens pas (pour l'instant) à utiliser un objet Vector (je crois que c'est comme ça que sa s'appel) car mon but est d'apprendre à me servir des classes template.

    Merci et au revoir.
    • Partager sur Facebook
    • Partager sur Twitter
      1 octobre 2007 à 17:15:12

      Pourquoi veux-tu copier la valeur de l'objet? Ce n'est pas un tableau de pointeurs? logiquement tu n'as qu'à copier la valeur du pointeur...

      Parcontre je verrais bien une méthode clone() qui permettrait de créer un tableau de pointeurs avec une copie de chaque éléments : dans ce cas :

      1) il te faut un constructeur par copie dans ta classe élément (ici c'est Armure)
      2) tu dois essayer de caster tes valeurs et tester le cast_exception ( utilise un static_cast<T>( *tab[ i ] ) ) dans un try-catch
      3) en cas d'exception tu détruit le nouveau tableau et tu relance l'exception
      4) si tout va bien tu retourne le nouveau tableau
      5) À l'appel tu le met aussi dans un try-catch, si tu reçois l'exception c'est que le tableau n'est pas valide.
      6) l'appel ce fait ainsi :
      1. pNouvTableau = ancienTableau.clone();


      La différence? C'est dans la définition de ce que fait la classe : un tableau de pointeur contient des pointeurs! Lorsqu'il se copie il copie ses éléments soit les pointeurs. Dans certains cas on veut une copie des valeurs pointée, c'est là qu'intervient la méthode clone().
      • Partager sur Facebook
      • Partager sur Twitter
        1 octobre 2007 à 18:24:23

        Merci.

        En fait ta méthode clone va faire éxactement ce que je veux que fasse mon constructeur de copie.
        En fait je ne veux pas copier la valeur du pointeur, car sinon mais deux tableaux pointeraient vers le même objets, or je veux qu'ils puissent agir indépendament.
        De manière plus concrète j'utilise cette classe pour gérer l'inventaire de personnages, donc quand je créer un personnage à partir d'un autre je veux aussi créer un nouvel inventaire, de nouvelles armes et armures et non pas utiliser les mêmes, car sinon lorsque je vais changer le contenu de l'inventaire d'un des deux personnages le contenu de l'autre va changer aussi.
        En plus j'aurais une erreur à la destruction des objets car le premier va être détruit et les objets vers lesquels il pointe aussi lorsque le deuxième va être détruit il va vouloir détruire des objets qui n'éxistent plus, donc bug.

        Pour ce qui est de ta proposition de la méthode clone (qui serait mon constructeur de copie), je ne me suis pas encore entraîner (je n'ai fait que lire la théorie) à utiliser les exeptions et je ne préfère pas m'y mettre maintenant de peut de m'embrouiller puisque je ne suis pas encore tout à fait au point avec les templates et les tableaux de pointeurs, mais merci quand même pour la proposition.

        Donc si quelqu'un connaît une autre manière de faire je suis preneur.

        Au revoir.
        • Partager sur Facebook
        • Partager sur Twitter
          1 octobre 2007 à 18:50:51

          Je suis un peu mélangé a vrai dire par ta classe parce que tu ne fais pas d'abstraction entre l'implémentation et l'utilisation...

          Moi si j'utilise ta classe comme j'utilise std::vector (i.e. sans connaître le code) quand je vois que la classe se nomme TableauPointeurs je m'attend à devoir créer mon pointeurs à l'extérieur de la classe et le détruire moi-même.

          je crois que je vais trouver une méthode ajouter( T* ) et non (const T & ).

          Je m'attend à devoir libérer la mémoire que j'ai alloué

          Je m'attend à pouvoir envoyé le tableau par copie en pouvant accéder à mes pointeurs que les objets pointés seront modifiés lorsque je reviendrai de la fonction...

          Mais tu implémente tout simplement un tableau d'objet et qu'en arrière plan tu utilises les pointeurs pour conserver tes éléments, c'est différent.

          En lisant le code je me suis aperçu que tu es toi-même mélanger : dans ta méthode d'ajout tu reçois une référence constante et tu essai de l'ajouter comme une pointeur (Buzzer de mauvaise réponse) Il faut faire un choix, soit que l'utilisateur ajoute un pointeur qui a instancié soit il te passe une copie de l'objet et toi tu instancie un pointeur

          Après ce choix tu va devoir ajuster la destruction, est-ce que tu dois supprimer ou non les objets pointés (si c'est une copie oui, sinon ce n'est pas à toi de le faire)

          et ainsi de suite avec le clone et/ou le constructeur par copie et/ou l'affection et/ou l'accès aux éléments

          En ajout : si le but est d'accéléré le processus en utilisant les pointeurs tu perds ce temps en incrémentant/décrémentant la taille du tableau à chaque fois que tu manipule le tableau.

          finalement : fait attention return (i++) != return (++i)

          • Partager sur Facebook
          • Partager sur Twitter
            1 octobre 2007 à 19:41:21

            Bon je suis désolé je ne vais pas pouvoir répondre correctement je dois m'en aller je voulais juste te remercier pour ces réponses et t'avertir que je réponderais à tes propositions plus tard.

            Je tiens tout de même a dire que je connais la différence entre ++i et i++ (si c'est la même qu'en PHP);
            1. int i = 5;
            2. cout << i++ << endl; //Affiche: 5
            3. cout << i << endl; //Affiche: 6
            4. cout << ++i << endl; //Affiche: 7


            Encore merci et je reflechirais à tout celà pour pouvoir répondre de manière complète demain.

            Au revoir.
            • Partager sur Facebook
            • Partager sur Twitter
              2 octobre 2007 à 0:44:45

              Pour ce genre de classes, bien réfléchir aux responsabilités. Et les documenter en gras, ou forcer les choses à l'aide de classes utilitaires sioux comme les std::auto_ptr<> qui riment avec sémantique de déplacement (à mettre dans un coin de votre tête pour quand vous aurez compris que les pointeurs ne sont pas vos amis)

              Sinon, quel est le véritable intérêt de cloner un personnage ou une arme ?

              Si c'est pour un intérêt théorique du genre "je pourrais en avoir besoin" => alors oublier tout de suite : cela ne servira jamais. Enfin si. Une telle perte de temps à un intérêt majeur : comprendre que ce n'est pas parce que l'on peut copier que l'on a besoin de copier. Et que du coup, mieux vaut souvent interdire pour être tranquille.

              Autant partir dessuite vers une solution de désérialization qui construira des personnages/armes/... depuis des données stockées dans des fichiers.
              • Partager sur Facebook
              • Partager sur Twitter
              C++: Blog|FAQ C++ dvpz|FAQ fclc++|FAQ Comeau|FAQ C++lite|FAQ BS| Bons livres sur le C++| PS: Je ne réponds pas aux questions techniques par MP.
                2 octobre 2007 à 6:53:40

                Encore une fois: merci pour les explications.
                Je vais réfléchir à tout celà dans la journé pour y voir plus clair.

                Sinon pour ta question lmghs quand à savoir quel est l'intéret de la copie:
                Dans mon cas aucun mais je me disais que à partir du moment où l'on develope une classe il faut qu'elle soit capable de gérer toutes les situations (c'est peut-être un mauvais postulat de départ...).
                Du coup j'ai voulus implémenter les constructeurs de copies en me disant "si quelqu'un le fait, il faut que celà fonctionne".

                Je vais donc repenser entièrement le fonctionnement et le but de mes classes histoire de mettre les choses à plat.

                Au revoir.
                • Partager sur Facebook
                • Partager sur Twitter
                  2 octobre 2007 à 8:34:06

                  A mes débuts, j'ai fait la même "erreur", et perdu des mois à vouloir définir des opérations de copie qui n'apportaient strictement rien d'utile à mon application. Je ne m'en suis d'ailleurs jamais servi -- de la possibilité de copier des réseau de neurones artificiel.

                  L'astuce est de réfléchir en termes d'entité ou valeur. Une valeur par définition se copie. Une entité, on a parfois besoin de la dupliquer (par clonage), mais c'est franchement rare.
                  • Partager sur Facebook
                  • Partager sur Twitter
                  C++: Blog|FAQ C++ dvpz|FAQ fclc++|FAQ Comeau|FAQ C++lite|FAQ BS| Bons livres sur le C++| PS: Je ne réponds pas aux questions techniques par MP.
                    2 octobre 2007 à 17:18:14

                    J'ai suivis vos conseils et repenser ma classe TableauPointeurs.
                    Résultat elle est maintenant plus sousple, elle s'appel simple Tableau et n'est pas restreinte au pointeur, tout dépend de la valeur du type fictif.

                    Je met le code comme il est pas très très long comme ça si le coeur vous en dit donner moi les critiques :
                    1. #ifndef DEF_TABLEAU
                    2. #define DEF_TABLEAU
                    3.         #include <iostream>
                    4.         template <class T>
                    5.         class Tableau
                    6.         {
                    7.                 protected:
                    8.                         T *v_tableau;
                    9.                         int v_nombre_entrees;
                    10.                         T* creerTableau (int nombre_entrees)
                    11.                                 { return new T[nombre_entrees]; };
                    12.                 public:
                    13.                         Tableau();
                    14.                         int ajouterEntree (T element);
                    15.                         void supprimerEntree (int index);
                    16.                         void viderTableau();
                    17.                         T getEntree (int index)
                    18.                                 { return v_tableau[index]; };
                    19.                         int getNombreEntrees()
                    20.                                 { return v_nombre_entrees; };
                    21.                         ~Tableau();
                    22.         };
                    23.         template <class T>
                    24.         Tableau<T>::Tableau()
                    25.                            : v_nombre_entrees (0)
                    26.         {
                    27.                 v_tableau = creerTableau (0);
                    28.         }
                    29.         template <class T>
                    30.         int Tableau<T>::ajouterEntree (T element)
                    31.         {
                    32.                 T *tableau_temporaire = creerTableau (v_nombre_entrees + 1);
                    33.                 for (int i = 0; i < v_nombre_entrees; ++i)
                    34.                         tableau_temporaire[i] = v_tableau[i];
                    35.                 tableau_temporaire[v_nombre_entrees] = element;
                    36.                 ++v_nombre_entrees;
                    37.                 delete[] v_tableau;
                    38.                 v_tableau = creerTableau (v_nombre_entrees);
                    39.                 v_tableau = tableau_temporaire;
                    40.                 //delete[] tableau_temporaire;
                    41.                 return (v_nombre_entrees - 1);
                    42.         }
                    43.         template <class T>
                    44.         void Tableau<T>::supprimerEntree (int index)
                    45.         {
                    46.                 T *tableau_temporaire = creerTableau (--v_nombre_entrees);
                    47.                 int i = 0;
                    48.                 for (int j = 0; j < (v_nombre_entrees + 1); ++j)
                    49.                 {
                    50.                         if (j == index)
                    51.                                 ++j;
                    52.                         if (i < v_nombre_entrees)
                    53.                                 tableau_temporaire[i] = v_tableau[j];
                    54.                         delete[] v_tableau;
                    55.                         v_tableau = creerTableau (v_nombre_entrees);
                    56.                         v_tableau = tableau_temporaire;
                    57.                         //delete[] tableau_temporaire;
                    58.                 }
                    59.         }
                    60.         template <class T>
                    61.         void Tableau<T>::viderTableau()
                    62.         {
                    63.                 delete[] v_tableau;
                    64.                 v_tableau = creerTableau (0);
                    65.                 v_nombre_entrees = 0;
                    66.         }
                    67.         template <class T>
                    68.         Tableau<T>::~Tableau()
                    69.         {
                    70.                 delete[] v_tableau;
                    71.         }
                    72.         //#include "../Tableau.ttp"
                    73. #endif


                    Encore une fois merci pour les conseils et puis je vais commencer à regarder du côté de la classe Vector et des "classes utilitaires sioux".
                    Il est préférable de commencer par quel bout?

                    Au revoir.

                    [EDIT]
                    J'allais oublier, si vous regardez ligne 51 et 73 il y as en commentaire delete[] tableau_temporaire.
                    Je l'ai enlevé, car sinon le debuger de C::B m'ouvrait une fenêtre avec pour titrer "Back-Trace" mais je comprend pas très bien pourquoi c'est une erreur, car j'ai créé un tableau de manière dynamique dans ma classe, je dois donc le détruire non ?

                    Du coup j'ai remplacer l'assignation des tableaux par:
                    1. delete[] v_tableau;
                    2. v_tableau = creerTableau (v_nombre_entrees);
                    3. //v_tableau = tableau_temporaire;
                    4. copierTableau (tableau_temporaire);
                    5. //delete[] tableau_temporaire;


                    Voic la définition de la fonction copierTableau:
                    1. template <class T>
                    2. void Tableau<T>::copierTableau (T *tableau)
                    3. {
                    4.         for (int i = 0; i < v_nombre_entrees; ++i)
                    5.                 v_tableau[i] = tableau[i];
                    6. }


                    Je trouve celà plus propre mais est-ce une bonne chose?
                    • Partager sur Facebook
                    • Partager sur Twitter
                      2 octobre 2007 à 23:21:57

                      OK. Alors en vrac des remarques.

                      a- Ce genre de classe se dérive extrèmement difficilement. Syntaxiquement, on arrive à des trucs, mais il y a toujours des pièges au tournant.
                      => c'est se mentir que d'utiliser la portée "protégé". "private" est plus juste.

                      b- pas de <iostream> dans un .h où tu n'utilises ni std::cin, ni std::cout, ni std::cerr, ni std::clog. Si tu as besoin d'une déclaration de std::ostream et/ou std::istream, alors inclus <iosfwd>. Si tu as besoin de la définition de std::istream et/ou std::ostream, alors inclus <istream> et/ou <ostream>.

                      c- Tes deux accesseurs devraient être "const" car ils ne modifient pas l'état de ton tableau.
                      De plus, si tu veux pouvoir modifier le i-ème élément, tu va devoir renvoyer une référence sur ce dernier.

                      Et si tu ne veux pas payer trop cher tes copies pour des gros objects, renvoie un "T const&".
                      Pour des int, cela pourrait couter plus cher, heureusement l'ininling devrait dégager tout coût ; sans compter que renvoyer une référence constante permet d'exploiter les algos standard (cf le tuto de r0d sur developpez pour ces derniers ; et aussi p.ex. les classiques std::copy(&v[0], &v[N], ....) qui marchent aussi sur des vecteurs non modifiables grâce au type retourné par l'opérateur [] de std::vector<>)

                      d- Très bien d'utiliser un temporaire avant d'utiliser une copie.

                      e- ton ajoute() coûte excessivement cher vu que tu réalloues à chaque nouvel élément. std::vector a une croissance logarithmique (si mes souvenirs sont bons) : i.e. cette classe distingue "capacité" et "nombre d'éléments" courants, et réalloue en gros le double de la capacité courante à chaque fois que l'on se sent à l'étroit.

                      f- Il ne faut JAMAIS modifier un index à l'intérieur d'une boucle, et dans le bloc d'itération d'un for si tu veux arriver à quelque chose.
                      Au choix,
                      - fais deux boucles
                      - distingue capacité et taille, et alors fait partir ton j de "index" directement (ou utilise l'algo de la SL de déplacement qui va bien)

                      g- A ne pas expliciter (interdire ou spécialiser pour ton cas) les deux opérations de copie, tu vas te rendre dans le mur. cf le tuto de m@téo sur la gestion de d'attributs de type ressource (et la FAQ de developpez (ou quelques archives sur fclc++, developpez, et ici) pour l'opérateur d'affectation)


                      PS: très bien pour un exo, mais dans du vrai code, utilises std::vector<>.
                      • Partager sur Facebook
                      • Partager sur Twitter
                      C++: Blog|FAQ C++ dvpz|FAQ fclc++|FAQ Comeau|FAQ C++lite|FAQ BS| Bons livres sur le C++| PS: Je ne réponds pas aux questions techniques par MP.
                        3 octobre 2007 à 20:48:07

                        Alors je reprends:

                        a - Okk pour la portée private je vais prendre l'habitude de l'utiliser si mes classes ne sont pas héritées.

                        b - Je prends note, en faite je croyais qu'il était necessaire pour utiliser les template, j'aurais dû tester. Pour le std::ostream et std::istream je retiens.

                        c - Oups j'ai oublié de les rajouter...
                        Pour la référence je n'y avais pas penser, mais utiliser une référence sur un pointeur ce n'est pas inutil ? Pour moi la référence sert soit à remplacer les pointeurs, pour plus de facilité, ou à éviter la copie lors du passage en paramètre.
                        Pour le retour d'une constante, je ne suis pas d'accord, car si je stock un pointeur d'objet et que je désir modifier cet objet, je vais appeler mon accesseur pour qu'il me renvoi le pointeur de l'objet et l'utiliser pour appeler une méthode de l'objet qui va se charger de modifier l'objet, donc si c'est une constante que je revois avec mon accesseur je vais avoir le droit à une erreur non ?

                        d - Je te remercie mais je ne vois pas trop comment faire autrement...

                        e - Il est donc plus avantageux d'avoir un tableau avec un surplus de mémoire réservé vis à vis du coût de la réallocation?

                        f - Oui j'avais aussi penser à faire deux boucles mais je sais plus pourquoi j'ai fait "ça" (en PHP c'est pas bien non plus de passer une valeur calculée comme condition de boucle).

                        g - Qu'appel tu les deux opérations de copies ? Le constructeur de copie et la surcharge de l'opérateur d'affectation ? Si oui comme je n'en ai pas utilité et que je ne le dévellope lus pour les gens qui aurait l'idée de la faire c'est pas un problème et pour ce qui est de la surcharge des opérateurs je préfère utiliser des fonctions pour plus de lisibilité (mais c'est peut-être un coût top important ?).

                        Oui, on m'as déjà dit d'utiliser le std::vector, mais là en effet c'est pour m'entraîner.

                        Merci.
                        • Partager sur Facebook
                        • Partager sur Twitter
                          4 octobre 2007 à 0:27:07

                          c- surcharge. version const + version non-const
                          Quand à l'intérêt, c'est de pouvoir offrir une vue "tableau d'éléments", soit une plage de données itérables, donc un truc utilisable avec les algos standard (et autres).
                          Pour les signatures, regarde les opérateurs [] de std::vector.

                          d- "Autrement" ? C'est la mauvaise méthode que 95% (99%?) des gens utilisent par défaut : détruire avant d'allouer.
                          Après, il y a d'autres façons un chouilla plus sioux qui passent par des objets temporaires de ton type, et par des swap. Approche typique pour définir l'opérateur d'affectation.

                          e- Définitivement

                          g- Oui, eux.
                          Et contrairement à ce que tu penses, tu peux en avoir besoin à tout moment à ton insu (i.e., au premier retour par valeur, au premier stockage dans un autre tableau, ...). Si tu penses sincèrement n'en avoir jamais besoin, alors interdis les (déclarées privées, sans définition). Et tu risques alors d'être surpris de t'en servir là où tu n'y avais pas pensé.
                          Ce n'est pas une question de coût, mais de sécurité.

                          • Partager sur Facebook
                          • Partager sur Twitter
                          C++: Blog|FAQ C++ dvpz|FAQ fclc++|FAQ Comeau|FAQ C++lite|FAQ BS| Bons livres sur le C++| PS: Je ne réponds pas aux questions techniques par MP.
                            4 octobre 2007 à 18:43:08

                            Bon alors je vais m'appliquer à faire les modifs.

                            Mais sinon je vais encore t'embêter un peut au sujet du c.
                            En fait il faut que je fasse une accesseur const et l'autre identique mais sans le const ?
                            Pour ce qui concerne les opérteurs [] j'ai fait une rechercher mais je n'ai rien trouvé (je suis malheureusement très peut doué pour ça), j'ai bien trouvé un tuto sur developer.com pour apprendre à utiliser std::vector mais la seule fois où il parles des opérateurs [] c'est pour dire qu'il faut faire attention car ils ne vérifient pas si l'index fournis est valide.
                            Donc si tu as un lien ou un truc dans le genre je suis preneur.

                            Merci et au revoir.

                            [EDIT]
                            Alors voilà après les modifications :
                            1. #ifndef DEF_TABLEAU
                            2. #define DEF_TABLEAU
                            3.         #define CAPACITE_SUPPLEMENTAIRE 20
                            4.         #define CAPACITE_CRITIQUE 2
                            5.         #define CAPACITE_AJOUTEE (CAPACITE_SUPPLEMENTAIRE - CAPACITE_CRITIQUE)
                            6.         template <class T>
                            7.         class Tableau
                            8.         {
                            9.                 private:
                            10.                         T *v_tableau;
                            11.                         int v_capacite;
                            12.                         int v_taille;
                            13.                         T* creerTableau (int capacite) const
                            14.                                 { return new T[capacite]; };
                            15.                         T* reserverMemoire() const
                            16.                                 { return creerTableau (v_capacite + CAPACITE_SUPPLEMENTAIRE); };
                            17.                         void copierTableau (T *tableau_temporaire);
                            18.                 public:
                            19.                         Tableau();
                            20.                         Tableau (const Tableau<T> &tableau_temporaire);
                            21.                         int ajouterEntree (T &element);
                            22.                         void supprimerEntree (int index);
                            23.                         void viderTableau();
                            24.                         Tableau<T> operator = (const Tableau<T> tableau_temporaire);
                            25.                         T &getEntree (int index) const
                            26.                                 { return v_tableau[index]; };
                            27.                         int getTaille() const
                            28.                                 { return v_taille; };
                            29.                         ~Tableau();
                            30.         };
                            31.         template <class T>
                            32.         Tableau<T>::Tableau()
                            33.                            : v_capacite (CAPACITE_SUPPLEMENTAIRE)
                            34.                            , v_taille (0)
                            35.         {
                            36.                 v_tableau = creerTableau (CAPACITE_SUPPLEMENTAIRE);
                            37.         }
                            38.         template <class T>
                            39.         Tableau<T>::Tableau (const Tableau<T> &tableau_temporaire)
                            40.                            : v_capacite (tableau_temporaire.v_capacite)
                            41.                            , v_taille (tableau_temporaire.v_taille)
                            42.         {
                            43.                 std::cout << "constructeur de copie" << std::endl;
                            44.                 v_tableau = creerTableau (v_capacite);
                            45.                 copierTableau (tableau_temporaire.v_tableau);
                            46.         }
                            47.         template <class T>
                            48.         int Tableau<T>::ajouterEntree (T &element)
                            49.         {
                            50.                 if ((v_capacite - v_taille) < 2)
                            51.                         v_capacite += CAPACITE_AJOUTEE;
                            52.                 T *tableau_temporaire = creerTableau (v_capacite);
                            53.                 for (int i = 0; i < v_taille; i++)
                            54.                         tableau_temporaire[i] = v_tableau[i];
                            55.                 tableau_temporaire[v_taille] = element;
                            56.                 v_taille++;
                            57.                 delete[] v_tableau;
                            58.                 v_tableau = creerTableau (v_capacite);
                            59.                 copierTableau (tableau_temporaire);
                            60.                 delete[] tableau_temporaire;
                            61.                 return (v_taille - 1);
                            62.         }
                            63.         template <class T>
                            64.         void Tableau<T>::supprimerEntree (int index)
                            65.         {
                            66.                 T *tableau_temporaire = creerTableau (v_capacite);
                            67.                 for (int i = 0; i < index; i++)
                            68.                         tableau_temporaire[i] = v_tableau[i];
                            69.                 for (int i = index; i < v_taille; i++)
                            70.                         tableau_temporaire[i] = v_tableau[i + 1];
                            71.                 v_taille--;
                            72.                 delete[] v_tableau;
                            73.                 v_tableau = creerTableau (v_capacite);
                            74.                 copierTableau (tableau_temporaire);
                            75.                 delete[] tableau_temporaire;
                            76.         }
                            77.         template <class T>
                            78.         void Tableau<T>::viderTableau()
                            79.         {
                            80.                 delete[] v_tableau;
                            81.                 v_tableau = creerTableau (CAPACITE_SUPPLEMENTAIRE);
                            82.                 v_capacite = CAPACITE_SUPPLEMENTAIRE;
                            83.                 v_taille = 0;
                            84.         }
                            85.         template <class T>
                            86.         void Tableau<T>::copierTableau (T *tableau_temporaire)
                            87.         {
                            88.                 for (int i = 0; i < v_taille; ++i)
                            89.                         v_tableau[i] = tableau_temporaire[i];
                            90.         }
                            91.         template <class T>
                            92.         Tableau<T> Tableau<T>::operator = (const Tableau<T> tableau_temporaire)
                            93.         {
                            94.                 std::cout << "surcharge operateur =" << std::endl;
                            95.                 v_capacite = tableau_temporaire.v_capacite;
                            96.                 v_taille = tableau_temporaire.v_taille;
                            97.                 v_tableau = creerTableau (v_capacite);
                            98.                 copierTableau (tableau_temporaire.v_tableau);
                            99.                 return *this;
                            100.         }
                            101.         template <class T>
                            102.         Tableau<T>::~Tableau()
                            103.         {
                            104.                 delete[] v_tableau;
                            105.         }
                            106.         //#include "../Tableau.ttp"
                            107. #endif


                            Je sais que les couts ne devraient pas être là mais comme vous vous en doutez c'était pour savoir si c'était le constructeur de copie ou la surcharge de l'opérateur d'affectation qui était appelé.
                            En l'occurence tout fonctionne mais il y as une petite bizarerie, ou sinon c'est tout à fait normal mais non précisé dans le tutos de mateo (ni dans mon livre, mais il faut dire que c'est un liver d'initiation lui aussi).
                            Alors voilà ce que j'ai d'affiché tout en haut de ma console lorsque je fait çà :
                            1. Tableau<Arme*> copie_arme;
                            2. copie_arme = arme;


                            Citation : Affichage dans la console

                            constructeur de copie
                            surcharge operateur =
                            constructeur de copie



                            C'est grave docteur?
                            • Partager sur Facebook
                            • Partager sur Twitter
                              5 octobre 2007 à 2:26:39

                              http://dinkumware.com/manuals/default.aspx?manual=compleat&page=vector.html#vector::operator[]

                              Sinon, pour l'affectation, ne renvoie pas de copie, ne prend pas de copie. (Tu dois pouvoir chercher "swap" dans ce forum, il n'y a pas grand monde sorti de moi à en avoir parlé). Tu trouveras une des écritures canonique de l'opérateur d'affectation.

                              Tu me parais avoir plus de delete[] que nécessaire.
                              N'hésite pas à faire des fonctions non plus. Typiquement, le ajouterElement() s'écrit:
                              1. void ajouterElement(T const& v) {
                              2.    if (this->taille() == this->capacite())
                              3.        this->agrandirCapacite();
                              4.    this->m_buffer[this->taille()] = v;
                              5.    ++this->m_taille;
                              6.    // NB: invariant de classe: taille() <= capacite()
                              7.    // this-> obligatoire avec les templates
                              8. }
                              9. void agrandirCapacite() { // écrit à la va-vite ; optimisable
                              10.     Tableau tmp(2*this->capacite() + K);
                              11.     std::copy( // <- #include<algorithm> ; memcpy() ne marchera pas
                              12.        this->getEntree(0), this->getEntree(this->taille()),
                              13.        tmp.getEntree(0));
                              14.     tmp->m_taille = this->taille();
                              15.     this->swap(tmp);
                              16.     // Nécessaire que getEntree() renvoie une référence, ou alors
                              17.     // attaquer directement m_buffer
                              18.     // NB: delete[] implicite dans le destructeur de tmp qui contient
                              19.     // l'ancien tableau au moment de sa destruction
                              20. }


                              PS: dans les toutes premières pages du forum, on avait parlé de ces problèmes avec Cyprien_. Cela concernait l'implémentaion d'une classe chaine, mais au fond, il s'agit à 98% de résoudre les mêmes problèmes.
                              • Partager sur Facebook
                              • Partager sur Twitter
                              C++: Blog|FAQ C++ dvpz|FAQ fclc++|FAQ Comeau|FAQ C++lite|FAQ BS| Bons livres sur le C++| PS: Je ne réponds pas aux questions techniques par MP.
                                5 octobre 2007 à 8:02:49

                                Merci, je regarderais le lien et le swap.

                                Pour le pointeur this, je ne saivais pas. J'ai vus dans le stl_vector.h qu'il y était tout le temps et je me demandais pourquoi, maintenant je sais.

                                Je mais ce sujet comme résolu, car j pense en avoir finis avec les bases des templates. Mon but n'étant pas d'en connaître tout les méandres, le reste viendras avec les cours et l'expérience.

                                Au revoir.
                                • Partager sur Facebook
                                • Partager sur Twitter
                                  5 octobre 2007 à 9:14:30

                                  Mais ... pratiquement aucune de mes remarques ne touchait les templates. :p
                                  • Partager sur Facebook
                                  • Partager sur Twitter
                                  C++: Blog|FAQ C++ dvpz|FAQ fclc++|FAQ Comeau|FAQ C++lite|FAQ BS| Bons livres sur le C++| PS: Je ne réponds pas aux questions techniques par MP.

                                  Classe template, la valeur fictive est un pointeur d'objet

                                  × Après avoir cliqué sur "Répondre" vous serez invité à vous connecter pour que votre message soit publié.
                                  × Attention, ce sujet est très ancien. Le déterrer n'est pas forcément approprié. Nous te conseillons de créer un nouveau sujet pour poser ta question.
                                  • Editeur
                                  • Markdown