IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

Langage C++ Discussion :

Copie de tableaux de type vector


Sujet :

Langage C++

  1. #1
    Membre du Club
    Inscrit en
    Août 2009
    Messages
    52
    Détails du profil
    Informations forums :
    Inscription : Août 2009
    Messages : 52
    Points : 40
    Points
    40
    Par défaut Copie de tableaux de type vector
    Bonjour tout le monde,

    Soient tab1 et tab2 deux tableaux de type vector:

    vector <int> tab1(5,3);
    vector <int> tab2;

    si on fait: tab2=tab1;

    cad on a copié tab1 dans tab2.

    1 ère question: est ce que tab2 et tab1 partagent le même emplacement mémoire? ou bien ils sont deux objets distincts?

    2ème question: Est ce qu' un objet vector est un tableau dynamique?


    Merci d'avance

  2. #2
    Expert éminent
    Homme Profil pro
    Ingénieur développement matériel électronique
    Inscrit en
    Décembre 2015
    Messages
    1 580
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 61
    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 580
    Points : 7 712
    Points
    7 712
    Par défaut
    Bonjour,

    1. En C++, l'opération de copie est Toujours une copie. Les 2 std::vector<> sont donc deux entités distinctes.
    2. Oui un std::vector<> est un tableau dynamique, son équivalent statique s'appelle std::array<>.

  3. #3
    Membre du Club
    Inscrit en
    Août 2009
    Messages
    52
    Détails du profil
    Informations forums :
    Inscription : Août 2009
    Messages : 52
    Points : 40
    Points
    40
    Par défaut
    Bonjour,

    Soit la classe tableau suivante:

    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 tableau{
     
        int * tab;
        int taille;
     
    public:
        tableau(int=20);
        ~tableau();
    }
     
    int main(){
        tableau v1, v2(5);
        v1=v2;
     
    }
    Quand on fait v1=v2; une copie membre à membre de v2 se fait dans v1 et cela s'applique même au membre tab qui est un tableau dynamique. Dans ce cas v1 et v2 partagent la même adresse mémoire pour le tableau.

    Est ce que pour résoudre ce problème on doit utiliser un constructeur par recopie et la surcharge de l'opérateur d'affectation =?

    Est ce que la classe Vector de C++ utilise une surcharge de l'opérateur = pour que si on écrit (tab1=tab2 qui sont de type vector) alors tab1 et tab2 seront deux objets distincts et qui ne partagent aucune adresse mémoire?



    Merci d'avance

  4. #4
    Expert éminent
    Homme Profil pro
    Ingénieur développement matériel électronique
    Inscrit en
    Décembre 2015
    Messages
    1 580
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 61
    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 580
    Points : 7 712
    Points
    7 712
    Par défaut
    En C++ on est sensé gérer des objets.
    Si tu utilises un pointeur (ce qui ne doit être fait que dans de rares cas, et où tu maitrises l'impact), en cas de copie tous les éléments sont copiés en particulier les pointeurs. Et copier un pointeur revient à pointer au même endroit, c'est pourquoi on ne dois pas utiliser les pointeurs. Donc l'exemple que tu donnes est ce qu'il ne faut jamais faire.

    On a en C++ des objets qui gèrent cela. Par exemple un std::vector<>:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    class tableau{
        std::vector<int>  tab;
     
    public:
       tableau(int nb=20) : tab(nb) {}
    }
    Avec un std::unique_ptr<>:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    class tableau{
        std::unique_ptr<int[]>  ptab;
        std::size_t  taille;
     
    public:
       tableau( std::size_t nb=20 ) : ptab{std::make_unique<int[]>(nb)}, taille{nb} {}
       tableau( tableau const& other ) : ptab{std::make_unique<int[]>(other.taille)}, taille{other.taille} {
          std::copy_n( other.ptab.get(), other.taille, ptab.get() );
       }
       tableau& operator=( tableau const& other ) {
           ptab = std::make_unique<int[]>(other.taille);
           taille = other.taille;
           std::copy_n( other.ptab.get(), taille, ptab.get() );
           return *this;
       }
    }
    J'ai du ajouter une "explication" sur comment faire pour faire une copie, sinon toute tentative de copier le tableau serait refusée. Mais pas besoin de destructeur défini explicitement, le std::uniqur_ptr<> gère tout ce qui concerne les allocations.

    Maintenant si tu veux toi même gérer directement un pointeur brut, ça devient vraiment plus compliqué, il te faut définir 5 fonctions voir la règle des 5.

    Et donc pour répondre à tes questions, il faut en effet définir à minima les constructeur par copie, opérateur d'affectation mais aussi le destructeur. C'est complexe avec une énorme probabilité de faire une erreur. Je fais du C++ depuis 35 ans, et je ne m'y risque pas.

    Et oui, la classe std::vector<> est un objet de la bibliothèque standard donc gère les règle des 5, d'où sait quoi faire pour être copiée, transférée et détruite proprement.

  5. #5
    Membre du Club
    Inscrit en
    Août 2009
    Messages
    52
    Détails du profil
    Informations forums :
    Inscription : Août 2009
    Messages : 52
    Points : 40
    Points
    40
    Par défaut
    Bonjour,

    Merci pour l'explication.

    Et pourquoi en C++ on utilise les références dans la surcharge des opérateurs et dans le constructeur par recopie? et pourquoi on utilise des références constantes?
    par exemple:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
     
     
    public:
    tableau( tableau const& other );
    tableau& operator=( tableau const& other );
    Merci d'avance

  6. #6
    Expert éminent
    Homme Profil pro
    Ingénieur développement matériel électronique
    Inscrit en
    Décembre 2015
    Messages
    1 580
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 61
    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 580
    Points : 7 712
    Points
    7 712
    Par défaut
    Citation Envoyé par nadia85 Voir le message
    Et pourquoi en C++ on utilise les références dans la surcharge des opérateurs et dans le constructeur par recopie? et pourquoi on utilise des références constantes?
    par exemple:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
     
    public:
    tableau( tableau const& other );
    tableau& operator=( tableau const& other );
    C'est forcément par référence.
    Imagine si c'était par copie.

    Le constructeur serait alors: Pour créer une copie d'un tableau:
    a) je crée une copie du tableau en tant que paramètre other
    b) puis je fais ce qu'il faut pour mettre à jour mon objet (le code de la fonction)
    Ça n'a pas de sens, créer copie à besoin de créer copie!

    Pour l'opérateur d'assignation: Pour assigner un tableau:
    a) je crée une copie du tableau reçu en paramètre
    b) puis je fais ce qu'il faut pour copier (le code de la fonction)
    Là ça marcherait. Mais convient que finalement on créerait un tableau copie puis on copierait cette copie (le paramètre other) dans l'objet. Donc ça ferait 2 copies!

    Et ce sont des références constantes car le tableau à copier n'est évidement pas modifié.

  7. #7
    Expert éminent sénior
    Homme Profil pro
    Analyste/ Programmeur
    Inscrit en
    Juillet 2013
    Messages
    4 670
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Bouches du Rhône (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Analyste/ Programmeur

    Informations forums :
    Inscription : Juillet 2013
    Messages : 4 670
    Points : 10 677
    Points
    10 677
    Par défaut
    J'espère que tu auras 1 bonne note avec toutes nos réponses

    Alors, on passe 1 référence pour justement éviter 1 copie parce que le passage de paramètres en C/ C++ est par copie/ par valeur par défaut.
    Et 1 référence est mieux qu'1 pointeur, parce qu'1 référence garantit que la référence est valide (avec le pointeur on peut avoir 1 pointeur NULL ou non initialisé)

    Évidemment le mot clef const permet de dire que la passage de paramètres est "en entrée" ("in" en anglais) (donc en lecture seule)
    Paramètre entrée ("in", lecture), sortie ("out", écriture), entrée-sortie ("in out", lecture et écriture), lien wikipedia en français

    Plus généralement, le mot clef const est la base du concept mutable/ immutable

    Et pour finir, lorsqu'1 objet est copié, est utilisé soit le constructeur de recopie (pour 1 nouvel objet) soit l'opérateur d'affectation (pour 1 variable déjà définie)
    Devine quoi si tu ne passes pas 1 référence dans le constructeur de recopie (const ou pas), tu vas appeler ce constructeur pour faire la copie du paramètre, qui va appeler .... tu as 1 boucle infinie

  8. #8
    Membre du Club
    Inscrit en
    Août 2009
    Messages
    52
    Détails du profil
    Informations forums :
    Inscription : Août 2009
    Messages : 52
    Points : 40
    Points
    40
    Par défaut
    Bonsoir,

    Merci pour toutes ces explications.

    J'ai une dernière question pour s'assurer d'une chose.

    quand j'ai :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
     
    vector <int> tab1(5,3);
    vector <int> tab2;
     
     tab2=tab1
    est ce que l'utilisation du symbole = dans ce cas est une surcharge de l'opérateur = dans la classe vector?

    Merci d'avance

  9. #9
    Expert éminent sénior
    Homme Profil pro
    Analyste/ Programmeur
    Inscrit en
    Juillet 2013
    Messages
    4 670
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Bouches du Rhône (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Analyste/ Programmeur

    Informations forums :
    Inscription : Juillet 2013
    Messages : 4 670
    Points : 10 677
    Points
    10 677
    Par défaut
    Apprends à utiliser des traces ou le debug (point d'arrêt, exécution pas à pas, ...) pour ce genre de questions

    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
    #include <iostream>
    #include <cstdlib>
     
     
    class Dummy {
    public :
     
        Dummy(int param=-1) : stuff(param) { std::cout << "debug - Dummy::Dummy default : " << stuff << std::endl; }
     
        Dummy(const Dummy& other) : stuff(other.stuff) { std::cout << "debug - Dummy::Dummy copy : " << stuff << std::endl; }
     
        Dummy& operator= (const Dummy& other) {
            stuff = other.stuff;
            std::cout << "debug - Dummy::operator= : " << stuff << std::endl;
     
            return (*this);
        }
     
    private:
     
        int stuff;
    };
     
     
    int main()
    {
        std::cout << "init d1 and d2" << std::endl;
        Dummy d1, d2(5);
     
        std::cout << "init d3 with d1" << std::endl;
        Dummy d3(d1);
     
        std::cout << "copy d2 in d3" << std::endl;
        d3 = d2;
     
     
        return EXIT_SUCCESS;
    }
    Sortie :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    init d1 and d2
    debug - Dummy::Dummy default : -1
    debug - Dummy::Dummy default : 5
    init d3 with d1
    debug - Dummy::Dummy copy : -1
    copy d2 in d3
    debug - Dummy::operator= : 5

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

Discussions similaires

  1. Copie d'un objet avec un attribut de type vector
    Par yassine480480 dans le forum C++
    Réponses: 7
    Dernier message: 02/08/2008, 09h53
  2. [Tableaux] Le type Ensemble existe-t-il ?
    Par petitnuage dans le forum Langage
    Réponses: 3
    Dernier message: 08/06/2006, 20h21
  3. Réponses: 6
    Dernier message: 17/03/2006, 12h23
  4. Réponses: 2
    Dernier message: 09/02/2006, 18h20
  5. D7 - Copy et tableaux dynamiques
    Par RamDevTeam dans le forum Langage
    Réponses: 2
    Dernier message: 15/08/2005, 13h46

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