[C++] Questions sur "new" et les arguments d'un constructeur

Questions sur "new" et les arguments d'un constructeur [C++] - C++ - Programmation

Marsh Posté le 04-01-2014 à 00:20:20    

Bonsoir :jap: ,
Je poste parce que je me pose des questions sur le sujet de l'utilisation d'un constructeur et l'utilisation de new (alloc dynamique) en même temps.
(Ca fait pas longtemps que j'ai fait du C++, mais j'ai fait du C, du Java et d'autres trucs avant)

 

J'ai en gros la classe suivante graph qui est une matrice 2D de booléen (les valeurs représentes les liens entre noeuds du graphe)

 
Code :
  1. class graph{
  2. private:
  3.     bool** edges;
  4.     int size;
  5. public:
  6.     graph(int size):size(size){
  7.         edges = new bool*[size];
  8.         for (int i = 0; i < size; i++){
  9.             edges[i] = new bool[size];
  10.         }
  11.     }
  12.     ~graph(){
  13.         for(int i = 0; i < size; ++i) {
  14.             delete [] edges[i];
  15.         }
  16.         delete [] edges;
  17.     }
 

Je suis passé par 2 boucles dans le constructeur parce qu'apparemment, on ne peut pas utiliser new avec un tableau multi-dimensionnel.
Y a une raison ? :jap:

 

Ensuite, je me demande :
A l'intérieur du constructeur, j'utilise l'allocation dynamique.

 

Mais dans mon main, avec l'instruction suivante

Code :
  1. graph graph1 (4);


Je fais une allocation statique, non ?
Du coup, il est ou mon objet au final ? Dans le stack ou dans le heap ?

 

Enfin, on en arrive à ma vraie question :
Quand je tente de faire par exemple : graph graph1 = new graph(5); ça me renvoie :
invalid conversion from 'graph*' to 'int' [-fpermissive])

 

Apparemment, new appelle pas de constructeur (faudrait que j'overloade new ? Donc que je fasse un constructeur par défault qui ferait de l'alloc statique et un new qui ferait l'alloc dynamique ? Je suis un peu paumé là.)

 

Au final, quelle est la bonne méthode pour initialiser mon tableau de liens si je veux allouer dynamiquement un graph ?
Je fais une méthode setEdges(int size) ?

 

C'est propre comme code de faire un constructeur qui peut laisser un attribut pointé dans le vide ?

 

Merci pour l'aide, et désolé pour les questions de noobs, je débarque en C++ :jap:
(J'avais fait de la POO en Java, mais je me posais pas de questions sur la gestion de la mémoire, j'ai commencé à m'en préoccuper quand j'ai fait du C).


Message édité par Profil supprimé le 04-01-2014 à 10:47:15
Reply

Marsh Posté le 04-01-2014 à 00:20:20   

Reply

Marsh Posté le 04-01-2014 à 13:19:40    

Concernant ta "vraie question", tu ne peut pas faire ça car new renvoit un pointeur sur l'objet alloué, donc tu dois faire :

Code :
  1. graph* graph1 = new graph(5);


Et tu accèderas aux méthodes en utilisant 'graph1->setEdge()' et à l'objet lui même (et pas au pointeur) en faisant '*graph1' (ex: "(*graph1).setEdge()" )
new appelle le constructeur approprié, je crois que t'as pas à te soucier de ça.
 
Pour l'allocation du tableau 2D c'est parce que tu alloue des emplacements contenant des pointeurs (1ère dimension), qu'il faut faire pointer vers des emplacement valides (je pense que tu trouveras plus clair sur stackoverflow). Une autre solution est de faire un vector de vector de bool, comme ça y'a pas d'allocation explicite et pas de pointeurs.
 
Sinon pour l'emplacement de ton objet en mémoire, je laisse quelqu'un de plus expérimenté répondre.

Reply

Marsh Posté le 04-01-2014 à 22:50:34    

Bonjour !
 
Dans l'exemple que vous citez, ce qui est dit est que si vous redéfinissez l'opérateur new dans une classe, il ne doit faire que l'allocation de la mémoire nécessaire à l'objet (pour faire votre propre gestion de la mémoire).
 
En revanche quand vous faites "toto = new Objet(x,y)",  les opérations suivantes sont effectuées :  
 * Appel de l'opérateur new de la classe Toto
 * Si la classe Toto est une classe fille, appel du constructeur de la classe mère
 * Appel du constructeur qui prend les paramètres x et y
 
Sinon, non, ce n'est pas propre de laisser un attribut (surtout un pointeur !) non initialisé, car aucune valeur par défaut n'est utilisée, la valeur de l'attribut sera celle de la case mémoire correspondante au moment où l'allocation est faite (quoique, en mode "debug", certains compilateurs initialisent ces attributs par défaut, mais pas en "release" ).
 
Bonne continuation, et bon courage pour la gestion de la mémoire, ce n'est pas toujours facile, on peut rapidement faire planter les programmes, mais on a vraiment la main, surtout quand on brasse pas mal d'allocations / désallocations, par rapport au Java où le GC fait (à peu près) ce qu'il veut ...
 
Sinon, sauf erreur de ma part, quand vous faites "graph graph1(4)", l'objet graph est alloué sur la pile, tandis que les "new" qui sont faits dans le constructeur sont alloués sur le tas.  
 
Je laisse des vrais experts corriger mes dires :)
 
Bonne continuation !


Message édité par Farian le 04-01-2014 à 22:50:46
Reply

Marsh Posté le 17-01-2014 à 14:38:33    

Bonjour,
 
Comme d’autres ont déjà répondu à ta question, je me permets de ne pas y répondre et simplement de te suggérer de ne jamais utiliser new lorsque l’on peut s’en passer -- c’est-à-dire quasiment tout le temps.
 
Dans l’exemple que tu donnes, par exemple, l’utilisation de std::vector s’impose pour allouer dynamiquement un tableau. Comme tu viens du C, l’abandon du réflexe de l’allocation manuelle est perturbante au début, mais libératoire au final.

Reply

Marsh Posté le 17-01-2014 à 15:19:32    

user89 a écrit :

Comme d’autres ont déjà répondu à ta question, je me permets de ne pas y répondre et simplement de te suggérer de ne jamais utiliser new lorsque l’on peut s’en passer -- c’est-à-dire quasiment tout le temps.


 
Mouais, une petite explication du pourquoi il faut éviter new, ça ne serait peut-être pas trop mal, nan ? La raison principale, c'est parce que ça ne fait pas bon ménage du tout avec les exceptions. Les symptomes typiques sont fuite mémoire et plantage plus que probable.
 
Par exemple dans ton code, si un new de ta boucle interne de ton constructeur retourne une exception, ton objet sera dans un état instable, tu seras incapable de déterminer quoi libérer. Au mieux fuite mémoire, au pire, plantage.
 
Le problème se pose aussi à l'utilisation. Avec un code du gerne :

Code :
  1. graph * graph1 = new graph(5);
  2.  
  3. /* Code travaillant sur graph1 */
  4.  
  5. delete graph1;


 
Si le code entre l'allocation et le delete retourne une exception non traitée dans la fonction => fuite mémoire. Avec un exemple aussi simple, c'est relativement évident. Lorsque les appels de fonctions commencent à s'empiler, il y intérêt à être prudent, si tu ne veux rajouter de try/catch tous les 2 lignes de codes.
 
Tandis qu'écrit de la sorte :
 

Code :
  1. graph graph1(5);
  2.  
  3. /* Code travaillant sur graph1 */


 
Si une exception se déclenche, ton destructeur sera appelé, pas besoin de pourrir le code avec try/catch, même pas besoin de delete, quelque soit la condition de retour de ta fonction. Ta variable ici, sera déclarée sur la pile (les champs edges et size), mais tes vecteurs seront bien alloués dynamiquement.
 

Reply

Marsh Posté le 17-01-2014 à 19:07:50    

De plus, utiliser new est une opération qui prend du temps (appels system) donc si tu peux t'en passer, faut pas hésiter.
 
Cela dit, ca reste indispensable dans tout de même pas mal de cas (par exemple lorsque que l'on utilise une interface ou une classe abstraite).

Reply

Marsh Posté le 17-01-2014 à 21:44:03    

tpierron a écrit :


Mouais, une petite explication du pourquoi il faut éviter new, ça ne serait peut-être pas trop mal, nan ? La raison principale, c'est parce que ça ne fait pas bon ménage du tout avec les exceptions.


 
La vrai raison principale est que chaque 'new' doit être suivi d'un 'delete', et qu'il existe de multiples façon d'oublier de le faire. Les exceptions en sont une, mais c'est loin d'être la seule, et c'est à mon avis pas celles qui sont les plus difficiles ni les plus courantes. Demoderateur venant du C, il doit bien connaître le problème des fuites mémoires alors même que le C ne possède pas d'exception.
 
Par ailleurs, même lorsque l'on travaille avec des classes abstraites, on peut (doit?) s'abstenir de faire des new et utiliser des pointeurs nu lorsqu'on le peut.

Reply

Sujets relatifs:

Leave a Replay

Make sure you enter the(*)required information where indicate.HTML code is not allowed