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 :

constructeur allocation dynamique tableau


Sujet :

C++

  1. #1
    Membre régulier
    Homme Profil pro
    Collégien
    Inscrit en
    Mars 2003
    Messages
    192
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Afghanistan

    Informations professionnelles :
    Activité : Collégien

    Informations forums :
    Inscription : Mars 2003
    Messages : 192
    Points : 87
    Points
    87
    Par défaut constructeur allocation dynamique tableau
    Salut,

    J'ai un objet ObjA qui contient, entre autre, un tableau d'objets B. Je me demande comment, dans le constructeur ObjA(), je peux allouer dynamiquement le tableau d'objets ObjB en appelant leur constructeur paramétré ObjB(int value).
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    class ObjA
    {
    int size;
    ObjB *B;
    };
     
     
    ObjA::ObjA()
    {
    //initialisation des membres de A
    size = 30;
    B = new ObjB[size];
    }
    En faisant ça, j'imagine que c'est le constructeur par défaut de ObjB qui est appelé pour créer le tableau de 'size' élements.

    Imaginons que la classe ObjB soit :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    class ObjB
    {
    int v;
    }
    Moi j'aimerais bien, qu'en construisant l'objet A, l'objet B soit alloué dynamiquement et que le membre 'v' soit l'indice du tableau d'objets B de la classe A... en appelant le constructeur suivant :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    ObjB::ObjB(int value)
    {
    v = value;
    }
    Je pourrais le faire en "deux coups" :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    ObjA::ObjA()
    {
    size = 30;
    B = new ObjB[size];
     
    for (int i=0; i < size; i++)
         {
         B.set_value(i);
         }  
    }
    avec
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    ObjB::set_value(int value)
    {
    v = value;
    }
    mais je me demande s'il y a un moyen plus "élégant" qui m'évite d'écrire explicitement cette boucle et de définir cette fonction B::set_value().


    J'espère avoir été clair.
    Merci de votre aide
    A bientot
    --
    Heimdall

  2. #2
    Membre émérite

    Inscrit en
    Mai 2008
    Messages
    1 014
    Détails du profil
    Informations forums :
    Inscription : Mai 2008
    Messages : 1 014
    Points : 2 252
    Points
    2 252
    Par défaut
    Bonjour,
    Citation Envoyé par Heimdall Voir le message
    mais je me demande s'il y a un moyen plus "élégant" qui m'évite d'écrire explicitement cette boucle et de définir cette fonction B::set_value().
    Il n'est pas possible de tout faire en une passe car new appelle obligatoirement le constructeur par défaut de chacun des objets du tableau. Par contre il est possible d'éviter de définir une fonction set_value comme ceci :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
     
    ObjA::ObjA()
    {
       size = 30;
       B = new ObjB[size];
     
       for (int i=0; i < size; i++)
       {
          B[i] = ObjB(i);
       }
    }

  3. #3
    Membre régulier
    Homme Profil pro
    Collégien
    Inscrit en
    Mars 2003
    Messages
    192
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Afghanistan

    Informations professionnelles :
    Activité : Collégien

    Informations forums :
    Inscription : Mars 2003
    Messages : 192
    Points : 87
    Points
    87
    Par défaut
    Salut et merci de ta réponse,


    Que fait exactement la ligne

    du point de vue de la mémoire ? Que devient la mémoire précédemment allouée par le new et le constructeur par defaut de ObjB lorsqu'on "écrase" B[i] par l'appel à ObjB(i) ?

    Nico
    --
    Heimdall

  4. #4
    Membre émérite

    Inscrit en
    Mai 2008
    Messages
    1 014
    Détails du profil
    Informations forums :
    Inscription : Mai 2008
    Messages : 1 014
    Points : 2 252
    Points
    2 252
    Par défaut
    Et bien on aura:
    qui va allouer size * sizeof(ObjB) à une adresse xxxxxxxx (l'adresse sera déterminées par l'allocateur système) puis va appeler le constructeur par défaut sur chacun des éléments du tableau. C'est à dire que ce bloc de mémoire à l'adresse xxxxxxxx et de taille size * sizeof(Obj) va passer d'un état initialisé (donc avec des octets aléatoire dedans) à un état ou chaque bout de la mémoire [k * sizeof(Obj), (k+1) * sizeof(Obj)] correspondra à un objet Obj, c'est à dire que l'on trouvera à la suite dans la mémoire chacun de ses données membres.
    Aussi on aura B == xxxxxxxx.
    Ce code va créer un objet temporaire de type ObjB en appelant le constructeur qui prend un int, puis va copier cet objet temporaire à l'adresse xxxxxxxx + i * sizeof(ObjB). Une fois la copie effectué l'objet temporaire est détruit. La copie se fait par le biais de l'opérateur=. Si tu ne l'as pas définit le compilateur en génère un par défaut qui fait de la copie membre à membre.

  5. #5
    Membre régulier
    Homme Profil pro
    Collégien
    Inscrit en
    Mars 2003
    Messages
    192
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Afghanistan

    Informations professionnelles :
    Activité : Collégien

    Informations forums :
    Inscription : Mars 2003
    Messages : 192
    Points : 87
    Points
    87
    Par défaut
    ok...

    imaginons qu'on ait un ObjB un peu plus compliqué que juste une variable int value, et qu'il y ait dedans un pointeur vers une zone mémoire.

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
     
    class ObjB
    {
    int *value;
    }
    Imaginons que le constructeur par défaut de ObjB() alloue 'value' a une taille par défaut :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
     
    ObjB::ObjB()
    {
    value = new int[100];
    }
    et qu'on a le constructeur paramétré suivant :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
     
    ObjB::ObjB(int i)
    {
    value = new int[i+1];
    }

    alors si le constructeur de ObjA est :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
     
    ObjA::ObjA()
    {
    size=30;
     
    B = new ObjB[size];
    for (int i=0; i<size; i++)
        B[i] = ObjB(i);
    }

    la ligne :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
     
    B = new ObjB[size]
    va allouer 'value' a 100 int pour chacun des 30 objets B

    puis la ligne

    va allouer des objets temporaires ObjB avec 'value' alloué à i+1 int, et le constructeur par copie va recopier les adresses de 'value' de ces objets temporaires dans B[i].value... et ainsi perdre l'accès à la mémoire précédemment allouée par le constructeur par défaut, non ?
    --
    Heimdall

  6. #6
    Rédacteur
    Avatar de 3DArchi
    Profil pro
    Inscrit en
    Juin 2008
    Messages
    7 634
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juin 2008
    Messages : 7 634
    Points : 13 017
    Points
    13 017
    Par défaut
    Salut,
    Il me semble que partir avec des std::vector et la panoplie de la STL semble résoudre assez bien ta problématique.

    Si B contient un pointeur alors il devrait correctement le gérer : cf F.A.Q. RAII et les tutos Gérer ses ressources de manière robuste en C++ par Aurélien Regat-Barrel et Présentation des pointeurs intelligents en C++ par Loïc Joly

    Donc, à un moment tu dois avoir un constructeur par copie, un opérateur = et un destructeur en cohérence avec la politique de gestion de ta ressource.

  7. #7
    Membre émérite

    Inscrit en
    Mai 2008
    Messages
    1 014
    Détails du profil
    Informations forums :
    Inscription : Mai 2008
    Messages : 1 014
    Points : 2 252
    Points
    2 252
    Par défaut
    Citation Envoyé par Heimdall Voir le message
    va allouer des objets temporaires ObjB avec 'value' alloué à i+1 int, et le constructeur par copie va recopier les adresses de 'value' de ces objets temporaires dans B[i].value... et ainsi perdre l'accès à la mémoire précédemment allouée par le constructeur par défaut, non ?
    Oui tout à fait.
    Dans ce cas précis (Obj contenant lui même un pointeur vers un tableau) alors l'opérateur= généré automatiquement par le compilateur est complètement faux car il va effectivement écraser le pointeur.
    Il faut donc obligatoirement définir soit-même :
    Un constructeur par copie
    Un opérateur=
    Un destructeur
    Avec le constructeur par copie et l'opérateur= qui recopie le tableau au lieu d’écraser le pointeur.

    Et c'est assez pénible à faire.
    De tête il faut faire à peu près comme ça (mais ça ne m’étonnerait pas d'avoir fait des erreurs )

    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
     
    struct ObjB
    {
       int size;
       int* value;
    };
     
    ~ObjB()
    {   
       delete[] value;
    }
     
    ObjB(const ObjB& other)
    {
        size = 0;
        value = NULL;
        if(other.size > 0 && other.value != NULL)
        {
             size = other.size;
             value = new int[size];
             std::copy(other.value, other.value + size, value);
        }
    }
     
    Obj& operator=(const Obj& other)
    {
        size = 0;
        if(value != NULL)
        {
            delete value;
            value =NULL ;
        }
     
        ObjB temp(other);
        std::swap(this->value, temp.value);
        std::swap(this->size, temp.size);
        return this; 
    }
    Bon comme tu le vois c'est assez pénible à faire soit-même (et aussi relativement difficile pour l'opérateur = ). C'est pour ça qu'en général je laisse tomber tout ça et utilise std::vector<T>.
    std::vector<T> est un wrapper d'un tableau dynamique T* et gère toutes ces questions (destructeur, constructeur par copie, opérateur= etc) lui-même. Et ça simplifie énormément ObjB car si ObjB contient un std::vector<int> au lieu d'un tableau dynamique alors son constructeur par copie et opérateur= généré automatiquement par le compilateur suffit.

  8. #8
    Membre régulier
    Homme Profil pro
    Collégien
    Inscrit en
    Mars 2003
    Messages
    192
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Afghanistan

    Informations professionnelles :
    Activité : Collégien

    Informations forums :
    Inscription : Mars 2003
    Messages : 192
    Points : 87
    Points
    87
    Par défaut
    ok merci beaucoup pour vos réponses.
    --
    Heimdall

Discussions similaires

  1. problème allocation dynamique tableau 2d
    Par virtual_bug dans le forum C++
    Réponses: 16
    Dernier message: 17/04/2012, 11h21
  2. Allocation dynamique tableau
    Par cstan dans le forum C++
    Réponses: 3
    Dernier message: 10/10/2011, 14h37
  3. Allocation dynamique tableau
    Par dream_of_australia dans le forum Débuter
    Réponses: 16
    Dernier message: 24/09/2009, 23h15
  4. Allocation dynamique: Tableau de pointeur sur char
    Par Anonymouse dans le forum Débuter
    Réponses: 4
    Dernier message: 21/10/2007, 10h57
  5. Allocation dynamique tableau dans structure
    Par chental dans le forum C
    Réponses: 2
    Dernier message: 03/08/2006, 09h03

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