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 :

Comment copier un objet contenant un pointeur ?


Sujet :

C++

  1. #1
    Membre éclairé Avatar de Matthieu76
    Homme Profil pro
    Consultant informatique
    Inscrit en
    Mars 2013
    Messages
    568
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Hauts de Seine (Île de France)

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

    Informations forums :
    Inscription : Mars 2013
    Messages : 568
    Points : 890
    Points
    890
    Par défaut Comment copier un objet contenant un pointeur ?
    Bonjour, j'ai encore une question à la *** à vous poser

    Si j'ai un pointeur vers un tableau dans mon objet, suis-je obligé de surdéfinir l'opérateur = pour pouvoir copier mon objet correctement ?

  2. #2
    Expert confirmé
    Inscrit en
    Mars 2005
    Messages
    1 431
    Détails du profil
    Informations forums :
    Inscription : Mars 2005
    Messages : 1 431
    Points : 4 182
    Points
    4 182
    Par défaut
    Tout dépend, veux-tu copier ton objet ou seulement ta collection de références ? Comment comptes-tu exploiter la copie du tableau ?

  3. #3
    Membre éclairé Avatar de Matthieu76
    Homme Profil pro
    Consultant informatique
    Inscrit en
    Mars 2013
    Messages
    568
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Hauts de Seine (Île de France)

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

    Informations forums :
    Inscription : Mars 2013
    Messages : 568
    Points : 890
    Points
    890
    Par défaut
    Je veux juste avoir un second objet qui est une copie conforme du premier.

  4. #4
    Rédacteur/Modérateur


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

    Informations professionnelles :
    Activité : Network game programmer

    Informations forums :
    Inscription : Juin 2010
    Messages : 7 115
    Points : 32 967
    Points
    32 967
    Billets dans le blog
    4
    Par défaut
    L'opérateur = par défaut fera appel à l'opérateur = de chaque membre.
    Si tu as un pointeur, il copiera le pointeur - pas les données pointées.
    Donc à ta question : ça dépend de ce que tu veux faire et fais.
    Comme par exemple les problèmes usuels d'appartenance/responsabilités des données du pointeur - et par extension du pointeur donc.
    Et nous on en sait rien.
    Pensez à consulter la FAQ ou les cours et tutoriels de la section C++.
    Un peu de programmation réseau ?
    Aucune aide via MP ne sera dispensée. Merci d'utiliser les forums prévus à cet effet.

  5. #5
    Membre éprouvé
    Profil pro
    Inscrit en
    Mai 2006
    Messages
    780
    Détails du profil
    Informations personnelles :
    Localisation : France, Haute Garonne (Midi Pyrénées)

    Informations forums :
    Inscription : Mai 2006
    Messages : 780
    Points : 1 176
    Points
    1 176
    Par défaut
    LA question étant de savoir si tu as vraiment besoin d'un pointeur?

  6. #6
    Membre éprouvé
    Inscrit en
    Avril 2005
    Messages
    1 110
    Détails du profil
    Informations forums :
    Inscription : Avril 2005
    Messages : 1 110
    Points : 937
    Points
    937
    Par défaut
    Citation Envoyé par Matthieu76 Voir le message
    Si j'ai un pointeur vers un tableau dans mon objet, suis-je obligé de surdéfinir l'opérateur = pour pouvoir copier mon objet correctement ?
    Sans plus de détails, en général on dira que "oui".
    Comme le préconise la fameuse désormais "règle des cinq"
    http://en.cppreference.com/w/cpp/language/rule_of_three

    Citation Envoyé par Matthieu76 Voir le message
    Je veux juste avoir un second objet qui est une copie conforme du premier.
    Jette un coup d'oeil au std::shared_ptr<>, il pourrait te servir.

  7. #7
    Expert éminent
    Homme Profil pro
    Ingénieur développement matériel électronique
    Inscrit en
    Décembre 2015
    Messages
    1 565
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 60
    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 565
    Points : 7 648
    Points
    7 648
    Par défaut
    Ce que tu dois faire, dépend de la stratégie de vie ton tableau.
    Si le tableau est statique (pas alloué dynamiquement), il faut laisser les pointeurs nus se copier et les 2 objets partageront le même tableau.
    Sinon, il faut impérativement un tableau alloué dynamiquement et :
    * tu veux que la copie produise un autre tableau ayant initialement le même contenu, utilise un std::unique_ptr et tu devras implémenter un opérateur et un constructeur de copie qui fabriqueront la copie.
    * tu veux que la copie partage le même tableau, utilise un std::shared_ptr, le système produira tout seul une copie qui fonctionne.
    * tu veux autre chose, tu devras gérer proprement les allocations à ta manière et par exemple utiliser des pointeurs nus mais il faut bien poser le problème et agir en pro.

  8. #8
    Membre éclairé Avatar de Matthieu76
    Homme Profil pro
    Consultant informatique
    Inscrit en
    Mars 2013
    Messages
    568
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Hauts de Seine (Île de France)

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

    Informations forums :
    Inscription : Mars 2013
    Messages : 568
    Points : 890
    Points
    890
    Par défaut re
    Merci pour vos réponses.

    J'ai opté pour la surcharge de l'opérateur =, c'était le plus rapide et le plus propre pour avoir des tableaux et pointeurs séparés.

    Faudra juste pas que j'oublie de rajouter chaque nouvelles variables dans ma surcharge mais à part ça c'est nickel

  9. #9
    Expert éminent sénior
    Avatar de koala01
    Homme Profil pro
    aucun
    Inscrit en
    Octobre 2004
    Messages
    11 614
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 52
    Localisation : Belgique

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 614
    Points : 30 626
    Points
    30 626
    Par défaut
    Salut,
    Citation Envoyé par Matthieu76 Voir le message
    Merci pour vos réponses.

    J'ai opté pour la surcharge de l'opérateur =, c'était le plus rapide et le plus propre pour avoir des tableaux et pointeurs séparés.

    Faudra juste pas que j'oublie de rajouter chaque nouvelles variables dans ma surcharge mais à part ça c'est nickel
    C'est peut être la solution (à condition que tu ait correctement implémenté l'opérateur =, et que tu aies pensé à implémenter correctement le constructeur de copie)... Ou non.

    Comme on te l'as dit : tout dépend de la stratégie de gestion que tu veux mettre en place.

    Et les deux toutes premières questions à se poser sont: as tu réellement besoin d'un pointeur une référence ne serait-elle pas largement préférable, vu qu'elle apporte la garantie de non nullité que ne peut pas apporter le pointeur

    Imaginons plusieurs cas :
    Soit tu as un tableau qui existe en dehors de ta classe, et tu veux pouvoir y accéder depuis n'importe quelle instance de ta classe : Dans ce cas, tu as sans doute intérêt à travailler avec une référence, sans doute sous une forme 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
    class MaClasse{
    public:
        explicit MaClasse(std::vector<TYPE> /* const */ & tab):tab_(tab){
        }
        /* on n'a besoin de rien d'autre : le compilateur créera lui même le constructeur de copie,
         * l'opérateur d'affectation et le destructeur qui vont bien, et cela nous satisfera pleinement
         */
    private:
        std::vector<TYPE> /* const */ & tab_;
    };
    /* et l'utilisation qui va avec */
    int main(){
        std::vector<TYPE> tab;
        MaClasse a(tab); // constructeur explicite
        MaClasse b(a); // creation d'une copie
        std::vector<TYPE> t2;
        MaClasse c(t2);
        c = b; // affectation
        return 0;
    }
    La deuxième série de question qu'il faut se poser, c'est : si tu utilises un pointeur, ne serait-ce pas parce que tu envisages d'avoir recours à la gestion manuelle de la mémoire, sous une forme qui pourrait être proche de
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    class MaClasse{
    public:
        MaClasse(size_t size): size_{size},ptr_{new TYPE [size]}{
        }
        /* il y a des trucs à prévoir ici : il faudra sans doute respecter la forme canonique orthodoxe de Coplien
         * et donc respecter la règles des trois grands (qui est devenue la règle des cinq grands depuis)
         */
    private:
        size_t size_; // la taille du tableau
        TYPE * ptr_; // le pointeur sur l'espace mémoire associé aux X éléments du tableau
    };
    Si tel est le cas, le seul conseil à te donner est : oublie directement cette solution!!! :

    La gestion manuelle de la mémoire est une véritable plaie en C++, et la bibliothèque standard nous fournit tout ce qu'il faut pour que nous n'ayons jamais à nous en inquiéter; entre autre, au travers de la classe std::vector.

    Mais bon, si tu ne veux absolument pas suivre ce conseil, il va falloir prendre des mesures pour garantir le bon fonctionnement de ta classe.

    La première chose, c'est qu'il faudra éviter les fuites mémoire lors de la destruction de l'instance de ta classe, si bien que nous devrons donc rajouter le destructeur, sous une forme proche de
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    class MaClasse{
    public:
        MaClasse(size_t size): size_{size},ptr_{new TYPE [size]}{
        }
        ~MaClasse(){
            delete [] ptr_;
        }
    private:
        size_t size_; // la taille du tableau
        TYPE * ptr_; // le pointeur sur l'espace mémoire associé aux X éléments du tableau
    };
    Et, comme on a défini un destructeur personnalisé, nous sommes tenus de respecter au minimum la règle des trois grands (qui est devenue la règle de cinq grand, depuis l'apparition de la sémantique de mouvement), à savoir
    Si, pour une raison ou une autre, nous devons définir un comportement particulier pour le constructeur de copie, pour l'opérateur d'affectation ou pour le destructeur, alors nous devons définir le comportement de ces trois notions
    La règle des cinq grands parle en outre également du constructeur de copie par déplacement et de l'opérateur d'affectation par déplacement

    Car, si on ne définit pas nous même le comportement du constructeur de copie et de l'opérateur d'affectation, le compilateur va fournir une implémentation "par défaut" de ces fonctions; et leur comportement respectif se contentera de copier les données "membre à membre". La notion de taille (la donnée membre size_) ne posera aucun problème : la copie (l'affectation) se fera parfaitement pour elle. Par contre, un pointeur n'est jamais qu'une donnée numérique entière (généralement non signée) représentant l'adresse mémoire à laquelle nous trouverons une donnée du type indiqué.

    Si bien que le constructeur de copie implémenté par défaut par le compilateur se contentera... de copier l'adresse mémoire représentée par le pointeur en question, et donc que deux instances de la même classe finiront par accéder à la même adresse mémoire, avec le résultat suivant :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    int main(){
        MaClasse a(12); // on alloue dynamiquement l'espace mémoire pour représenter 12 éléments
        MaClasse b(a); //CRACK a.ptr_ et b.ptr_ pointent sur la même adresse mémoire
    } // BOUM : ~MaClasse() est appelé pour b : l'adresse pointée par b.ptr_ est libérée et rendue au système d'exploitation
       //        ~MaClasse() est ensuite appelé pour a : on essaye de libérer l'adresse mémoire pointée par a.ptr_... Mais elle a déjà été rendue au système d'exploitation
       //        -->double free corruption
    Pour corriger ce problème, nous devons faire en sorte que chaque instance de notre classe puisse disposer d'un espace mémoire qui lui est propre. Nous devrons donc définir notre propre constructeur de copie, sous une forme qui serait 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
    class MaClasse{
    public:
        explicit MaClasse(size_t size): size_{size},ptr_{new TYPE [size]}{
        }
        MaClasse(MaClasse const & other):size_{other.size_}, ptr_{new TYPE[other.size_]}{
            /* il faut copier l'ensemble des données de other.ptr_ dans this->ptr_...
             */
        }
        ~MaClasse(){
            delete [] ptr_;
        }
    private:
        size_t size_; // la taille du tableau
        TYPE * ptr_; // le pointeur sur l'espace mémoire associé aux X éléments du tableau
    };
    Cette modification corrige le problème dont je vient de parler, car les instances a et b disposent toutes les deux d'un espace mémoire qui leur est propre, mais il reste un autre problème... En effet, si on écrit un code proche de
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    int main(){
        MaClasse a(10); // un espace de 10 éléments pour a.ptr_
        MaClasse b(15); // un espace de 15 éléments pour b.ptr_;
        b=a; // CRACK : 1- b.ptr_ pointe sur les dix mêmes éléments que a.ptr_
                //                2- on perd l'adresse qui était à l'origine représentée par b.ptr_ -->
                //                 fuite mémoire
        /* ... */
    } // BOUM : ~MaClasse() est appelé en premier pour b : l'espace mémoire commencant à b.ptr_ est rendu au système d'exploitaiton
       //        ~MaClasse() est ensuite appelé pour a : Mais comme a.ptr_ == b.ptr_, on essaye de rendre un espace mémoire qui a déjà été rendu au système d'exploitaiton
       //        -->double free corruption
    Il faut donc également redéfinir l'opérateur =, et, le plus facile pour éviter les problèmes est d'implémenter ce que l'on appelle l'idiome "copy and swap" : on crée d'abord une copie de l'objet, puis on interverti les données de l'objet qui subit l'affectation et de la copie que l'on vient de créer, sous une forme 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
    class MaClasse{
    public:
        explicit MaClasse(size_t size): size_{size},ptr_{new TYPE [size]}{
        }
        MaClasse(MaClasse const & other):size_{other.size_}, ptr_{new TYPE[other.size_]}{
            /* il faut copier l'ensemble des données de other.ptr_ dans this->ptr_...
             */
        }
        MaClasse & operator = (MaClasse const & other) {
            /* on crée un copie parfaite de other */
            MaClasse copy(other);
           /* on interverti les donnée de la copie et de l'objet courant */
          copy.swap(*this);
         /* on renvoie l'objet courant */
         return  *this;
       } // la copie est détruite ici, l'adresse mémoire qui était à l'origine allouée à l'objet courant est correctement libérée
        ~MaClasse(){
            delete [] ptr_;
        }
        /* la vrai magie intervient dans cette fonction ci */
        void swap(MaClasse & other){
            std::swap(size_,other.size_); // on interverti les tailles
            std::swap(ptr_, other.ptr_);  // et surtout les adresses mémoire qui sont pointées
        }
    private:
        size_t size_; // la taille du tableau
        TYPE * ptr_; // le pointeur sur l'espace mémoire associé aux X éléments du tableau
    };
    Voilà dans le détail ce qu'il faudrait faire si tu pars du principe que chaque instance de ta classe doit être responsable de l'espace mémoire qui lui est alloué pour représenter un tableau de taille inconnue à la compilation.

    NOTA: normalement, je devrais également te parler du constructeur de copie par déplacement et de l'opérateur d'affectation par déplacement... Mais comme il ne s'agit en définitive que de permettre certaines optimisations dans des situations très particulières, et que c'est déjà un sujet bien plus pointu, je crois que je vais m'en passer ici

    Mais, comme je te l'ai dit : il ne faut pas partir sur ce genre d'approche : la classe std::vector fait tout cela de manière totalement transparente pour toi. Il est donc ** peut-être ** sympa de le faire une fois (pour savoir comment il faut faire), mais, au delà de l'aspect théorique et didactique (pour lequel je ne suis pas certain qu'il existe réellement), tu auras surement des choses bien plus importantes auxquelles t'attarder que cela
    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

  10. #10
    Membre éclairé Avatar de Matthieu76
    Homme Profil pro
    Consultant informatique
    Inscrit en
    Mars 2013
    Messages
    568
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Hauts de Seine (Île de France)

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

    Informations forums :
    Inscription : Mars 2013
    Messages : 568
    Points : 890
    Points
    890
    Par défaut re
    Merci beaucoup, ta réponse VRAIMENT très conplète

    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
    class Perceptron
    {
        private:
            float* weights;
    }
     
    Perceptron::Perceptron(int numberOfInputs)
    {
        weights = new float[numberOfInputs];
        for (int i = 0; i < numberOfInputs; i++)
        {
                weights[i] = ( rand()/(float)RAND_MAX ) * 2 - 1;
        }
    }
     
    int Perceptron::output(float* inputs)
    {
        float sum = 0;
        for (int i = 0; i < numberOfInputs; i++)
        {
            sum += inputs[i] * weights[i];
        }
        sum += bias;
        return sum;
    }
    En réalité mon pointeur n'est là que pour pouvoir initialiser la taille de mon tableau dans mon constructeur.
    Je m'étais déjà dis qu'un vecteur solutionnerais mon problème mais en faite j'appelle ma fonction output plus de 700 fois par seconde car je fais des calculs mathématiques énormes et je me suis dis qu'utiliser un vecteur ralentirais surement mes étapes de calculs.

    Je sais pas si c'est vraiment bien de résonner comme ça mais je pense qu'un pointeur est plus rapide à manipuler qu'un vecteur.
    De toute façon mon objet ne devrait pas bouger donc je vais rester comme ça et si un jour j'ai des grosses modif à faire j'envisagerais le vecteur.

  11. #11
    Expert éminent sénior
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Février 2005
    Messages
    5 071
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 52
    Localisation : France, Val de Marne (Île de France)

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : Conseil

    Informations forums :
    Inscription : Février 2005
    Messages : 5 071
    Points : 12 116
    Points
    12 116
    Par défaut
    Laisses tomber ce pointeur tout moisi.
    Un vecteur "pré-alloué" (en spécifiant sa taille initiale) est tout aussi rapide et bien plus safe.
    700 fois/ secondes, c'est vraiment peanuts.

  12. #12
    Rédacteur/Modérateur


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

    Informations professionnelles :
    Activité : Network game programmer

    Informations forums :
    Inscription : Juin 2010
    Messages : 7 115
    Points : 32 967
    Points
    32 967
    Billets dans le blog
    4
    Par défaut
    Citation Envoyé par Matthieu76 Voir le message
    En réalité mon pointeur n'est là que pour pouvoir initialiser la taille de mon tableau dans mon constructeur.
    Je m'étais déjà dis qu'un vecteur solutionnerais mon problème mais en faite j'appelle ma fonction output plus de 700 fois par seconde car je fais des calculs mathématiques énormes et je me suis dis qu'utiliser un vecteur ralentirais surement mes étapes de calculs.

    Je sais pas si c'est vraiment bien de résonner comme ça mais je pense qu'un pointeur est plus rapide à manipuler qu'un vecteur.
    De toute façon mon objet ne devrait pas bouger donc je vais rester comme ça et si un jour j'ai des grosses modif à faire j'envisagerais le vecteur.
    Tu penses faux et tu peux très bien et devrais utiliser un vector au lieu du pointeur nu. Ne serait-ce que pour prendre les bonnes habitudes vu que tu débutes.
    L'overhead de std::vector en release est pour ainsi dire nul (en fait il est carrément nul même si tu fais pas n'importe quoi avec les options de compilation), tant que tu l'utilises correctement (adios .at, bonjour []).
    Et il suffit de voir l'implémentation de l'opérateur [] pour s'en assurer.
    VS2015 sur ma plateforme actuelle implémente
    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
    	const_reference operator[](size_type _Pos) const
    		{	// subscript nonmutable sequence
     #if _ITERATOR_DEBUG_LEVEL == 2
    		if (size() <= _Pos)
    			{	// report error
    			_DEBUG_ERROR("vector subscript out of range");
    			_SCL_SECURE_OUT_OF_RANGE;
    			}
     
     #elif _ITERATOR_DEBUG_LEVEL == 1
    		_SCL_SECURE_VALIDATE_RANGE(_Pos < size());
     #endif /* _ITERATOR_DEBUG_LEVEL */
     
    		return (*(this->_Myfirst() + _Pos));
    		}
    Pensez à consulter la FAQ ou les cours et tutoriels de la section C++.
    Un peu de programmation réseau ?
    Aucune aide via MP ne sera dispensée. Merci d'utiliser les forums prévus à cet effet.

  13. #13
    Membre éclairé Avatar de Matthieu76
    Homme Profil pro
    Consultant informatique
    Inscrit en
    Mars 2013
    Messages
    568
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Hauts de Seine (Île de France)

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

    Informations forums :
    Inscription : Mars 2013
    Messages : 568
    Points : 890
    Points
    890
    Par défaut J'ai testé, j'ai détesté :D (trop fière de mon jeu de mot)
    Ouais bah vos solution de merde, hein !!!! (je rigole bien sûr)

    J'ai remplacé tout mes pointeurs par des vecteurs et je dirais qu'à vu d’œil c'est 2 à 3 fois moins rapide qu'avant
    Je sais ce que que vous allez dire, que c'est moi qui sait pas utiliser les vecteurs mais j'ai juste remplacé mes pointeurs et je n'initialise aucun vecteur dans ma boucle principale.
    Bah de toute façon j'ai la flemme de tout remettre comme avant (même si j'ai évidemment sauvegardé mon ancien code).

    Je vais aussi regarder si je peux pas utilisé une pile ou une queue ou un truc du genre qui pourrais être plus rapide et mieux adapter à mon algorithme.
    En tout cas merci à vous tous, j'aime ce forum <3

  14. #14
    Rédacteur/Modérateur


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

    Informations professionnelles :
    Activité : Network game programmer

    Informations forums :
    Inscription : Juin 2010
    Messages : 7 115
    Points : 32 967
    Points
    32 967
    Billets dans le blog
    4
    Par défaut
    Faudrait en dire plus, parce que combien de débutants j'ai vu se plaindre que vector est soit-disant plus lent alors qu'ils calculaient juste mal leur truc, lisaient à moitié et oubliaient toujours la partie "reserve" ou étaient... en debug.
    Le fait est que vector est totalement inliné et similaire à un tableau nu avec les bonnes options de compilation (O2 ou O3 de mémoire).
    Il s'agit aussi de la collection qui devrait toujours être le premier choix pour implémenter une collection quelconque, ne serait-ce que grâce à la contiguité des éléments elle est très performante dans la plupart des cas.
    Pensez à consulter la FAQ ou les cours et tutoriels de la section C++.
    Un peu de programmation réseau ?
    Aucune aide via MP ne sera dispensée. Merci d'utiliser les forums prévus à cet effet.

  15. #15
    Membre expert
    Profil pro
    Inscrit en
    Mars 2007
    Messages
    1 415
    Détails du profil
    Informations personnelles :
    Localisation : France, Paris (Île de France)

    Informations forums :
    Inscription : Mars 2007
    Messages : 1 415
    Points : 3 156
    Points
    3 156
    Par défaut
    Citation Envoyé par Matthieu76 Voir le message
    J'ai remplacé tout mes pointeurs par des vecteurs et je dirais qu'à vu d’œil c'est 2 à 3 fois moins rapide qu'avant
    2 choses:
    • Il ne faut pas oublier de pré-allouer le vecteur (vector::reserve ou vector::resize, ou le bon constructeur, ça dépend de ton algo)
    • Il ne faut pas oublier de mesure la perf en mode Release.


    Assures-toi bien de ces deux points déjà et après compare les perfs
    Find me on github

  16. #16
    Membre éclairé Avatar de Matthieu76
    Homme Profil pro
    Consultant informatique
    Inscrit en
    Mars 2013
    Messages
    568
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Hauts de Seine (Île de France)

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

    Informations forums :
    Inscription : Mars 2013
    Messages : 568
    Points : 890
    Points
    890
    Par défaut merci
    Ça change quelque chose d'être mode realese ou debug sous Qt ?????
    Mec je crois que tu viens de révolutionner ma vie !!!!
    J'ai pas pré-allouer mes vecteurs mais je les ai remplie avec des 0, ça reviens au même, non ? (même si c'est moins "pro")
    Et en fait j'ai aussi modifier des petits trucs et c'est peut-être ça qui ralentie
    Désolé, je suis de mauvaise fois

  17. #17
    Expert éminent sénior
    Avatar de koala01
    Homme Profil pro
    aucun
    Inscrit en
    Octobre 2004
    Messages
    11 614
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 52
    Localisation : Belgique

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 614
    Points : 30 626
    Points
    30 626
    Par défaut
    Citation Envoyé par Matthieu76 Voir le message
    Ça change quelque chose d'être mode realese ou debug sous Qt ?????
    Que ce soit sous Qt ou quoi ou qu'est-ce, bien sur : le code destiné au débugage est automatiquement rajouté par le compilateur en mode debug.

    Pire, le code de Qt en lui-même est beaucoup plus lent en debug qu'en release, car Qt aussi rajoute un tas d'informations dans ce mode

    Donc, oui, si tu ne prend pas les bonnes options de compilation, tu remarquera toujours une très sévère perte de performances quand tu es en mode debug
    Mec je crois que tu viens de révolutionner ma vie !!!!
    J'ai pas pré-allouer mes vecteurs mais je les ai remplie avec des 0, ça reviens au même, non ? (même si c'est moins "pro")
    Quoi? tu as fais un truc du genre de
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    std::vector<int> tab;
    for(int i=0;i<MAXSIZE; ++i )
        tab.push_back(0);
    Si c'est le cas, que crois tu que cette boule apporte comme avantage par rapport à une boucle d'ajout des valeurs correctes qui t'intéressent

    Pire, là où tu aurais pu ajouter tes valeurs en une seule fois, tu te retrouve à les ajouter en deux fois : une fois pour remplire ton tableau de 0, et une fois pour le remplire avec les bonnes valeurs.

    Non, la solution consiste à utiliser resize / reserve pour que le tableau sache qu'il aura besoin d'un espace mémoire suffisant pour représenter N éléments (ces fonctions agrandiront en une fois suffisamment l'espace mémoire interne de std::vector pour qu'il puisse représenter le nombre d'éléments indiqués), puis à placer les valeurs qui t'intéressent dedans.

    De cette manière, à part une tout petite perte de temps (*) passée à demander l'espace mémoire nécessaire, le reste ne te demande qu'une seule boucle

    (*) Et comme, grâce à elle, ton tableau ne devra plus augmenter sa taille, cette perte de temps ne se fait plus qu'une fois au lieu de se faire un nombre de fois bien plus important... Donc, tu y gagne encore
    Désolé, je suis de mauvaise fois
    Oui, très certainement
    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

  18. #18
    Rédacteur/Modérateur
    Avatar de JolyLoic
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Août 2004
    Messages
    5 463
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 49
    Localisation : France, Yvelines (Île de France)

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Août 2004
    Messages : 5 463
    Points : 16 213
    Points
    16 213
    Par défaut
    Une erreur que j'ai souvent vue quand des gens passent d'un pointeur à un vecteur, c'est au moment de passer un argument à une fonction. Là où le pointeur était passé par valeur, il faut généralement passer le vecteur par référence (constante ou pas selon le cas).
    Ma session aux Microsoft TechDays 2013 : Développer en natif avec C++11.
    Celle des Microsoft TechDays 2014 : Bonnes pratiques pour apprivoiser le C++11 avec Visual C++
    Et celle des Microsoft TechDays 2015 : Visual C++ 2015 : voyage à la découverte d'un nouveau monde
    Je donne des formations au C++ en entreprise, n'hésitez pas à me contacter.

  19. #19
    Membre expert
    Profil pro
    Inscrit en
    Mars 2007
    Messages
    1 415
    Détails du profil
    Informations personnelles :
    Localisation : France, Paris (Île de France)

    Informations forums :
    Inscription : Mars 2007
    Messages : 1 415
    Points : 3 156
    Points
    3 156
    Par défaut
    Citation Envoyé par Matthieu76 Voir le message
    J'ai pas pré-allouer mes vecteurs mais je les ai remplie avec des 0, ça reviens au même, non ? (même si c'est moins "pro")
    Tout dépend de la manière . Si comme le dit Koala, tu l'as fais avec une boucle, tu te tires une balle dans le pied, car tu payes le même coût. Par contre, tu peux le faire à la construction du vecteur avec le bon constructeur. Quand tu connais à l'avance le nombre d'éléments du vecteur, ou si tu peux le prédire de manière assez précise, il faut préallouer, car sinon tu vas payer des réallocations/déplacements pour rien.

    Sinon, en terme de Debug, oui Qt sera meilleure en Release. Mais aussi la STL qui est bourrée de templates et qui, une fois compilée en release, profitera beaucoup de l'inlining.
    Find me on github

  20. #20
    Membre éclairé Avatar de Matthieu76
    Homme Profil pro
    Consultant informatique
    Inscrit en
    Mars 2013
    Messages
    568
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Hauts de Seine (Île de France)

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

    Informations forums :
    Inscription : Mars 2013
    Messages : 568
    Points : 890
    Points
    890
    Par défaut
    Citation Envoyé par JolyLoic Voir le message
    Une erreur que j'ai souvent vue quand des gens passent d'un pointeur à un vecteur, c'est au moment de passer un argument à une fonction. Là où le pointeur était passé par valeur, il faut généralement passer le vecteur par référence (constante ou pas selon le cas).
    Merci, maintenant que tu le dis je suis convaincu que mon ralentissement viens de là, car ça copie mes vecteurs à chaque passage en paramètre c'est hyper long.



    Citation Envoyé par koala01
    Quoi? tu as fais un truc du genre de

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    std::vector<int> tab;
    for(int i=0;i<MAXSIZE; ++i )
        tab.push_back(0);
    Si c'est le cas, que crois tu que cette boule apporte comme avantage par rapport à une boucle d'ajout des valeurs correctes qui t'intéressent
    Pour te répondre, je fais ce code constructeur ce qui me permet d’appeler la méthode suivante plusieurs fois de suite.
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    void objet::train()
    {
        for(int i=0;i<tab.size(); ++i )
            tab[i] = 42;
    }
    Comme ça cela m'évite de faire un clear et un pushback à chaque appel de ma fonction.

+ Répondre à la discussion
Cette discussion est résolue.
Page 1 sur 2 12 DernièreDernière

Discussions similaires

  1. Réponses: 8
    Dernier message: 16/11/2013, 19h49
  2. Réponses: 3
    Dernier message: 31/10/2008, 14h17
  3. Réponses: 11
    Dernier message: 29/09/2008, 10h57
  4. [C#]Comment puis-je copier un objet ?
    Par lanuage dans le forum C#
    Réponses: 12
    Dernier message: 03/11/2006, 20h11
  5. Réponses: 4
    Dernier message: 27/10/2006, 17h03

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