[C++] VCC capricieux, et borland....

VCC capricieux, et borland.... [C++] - Programmation

Marsh Posté le 03-03-2002 à 14:18:02    

Borland, me fait une erreur lors a la fin de l execution de mon programme:
 
ca se produit a cause du destructeur que j appelle moi meme :
(et qui serait donc reappelé une seconde fois puisque le programme se termine)
 
HI::~HI(void)
{
 if (nb != NULL) {delete nb ;nb=NULL;}
 taille=0  ;
}
 
class HI
{
 public:
   char * nb                    ;
   unsigned int taille          ;
 
   HugeInt::HugeInt(int a)      ;
   HugeIntt::~HugeInt(void)     ;
}
 
HI::HI(int a)
{
  nb = new char[a];
  taille = a;
}
 
avec VCC, j ai un autre pb :
quand je surcharge l operateur "=" par exemple, il plantage a l execution si le prototype de la fonction est:
void HI::operator=(HI hi);
 
mais pas si son prototype est :
void HI::operator=(HI &hi);
 
... sachant, que je voudrais relalisé l operation :
HI hi1,hi2;
hi1=hi2;  
etant donné que ca ne va pas modifier le parametre, pkoi veut il qu on le lui passe en tant que "reference" :??:
 
 
le plus curieux, c est que sous borland y a le pb du destructeur, mais pas de l operateur, et que sous vcc c est exactement l inverse...
 
voila, je pense que ce sont pourtant des logiciels reputés ? donc c est surment moi qui ai du me planté, mais la j avoue que je capte pas....

Reply

Marsh Posté le 03-03-2002 à 14:18:02   

Reply

Marsh Posté le 03-03-2002 à 14:32:23    

Pour le destructeur, je dirais que tu doit faire un delete [] np; parceque tu alloues un tableau et pas un simple char.
 
Pour l'opérateur d'affectation ( ainsi que le constructeur par recopie ), je dirais qu'il faut autant que possible passé le paramètre par reference sur un objet constant parceque le système peut utiliser ces opérateur quand tu passes un objet en paramètre à une fonction. Et puis de toute façon, c'est comme ça qu'il faut faire et pas autrement :)

Reply

Marsh Posté le 03-03-2002 à 14:34:39    

slvn a écrit a écrit :

avec VCC, j ai un autre pb :
quand je surcharge l operateur "=" par exemple, il plantage a l execution si le prototype de la fonction est:
void HI::operator=(HI hi);
 
mais pas si son prototype est :
void HI::operator=(HI &hi);




Dans le cas 1 il va passer par le constructeur par copie (HI::HI(const HI&) ) pour instancier l'objet local hi mais pas dans le second cas.

 

[jfdsdjhfuetppo]--Message édité par Verdoux--[/jfdsdjhfuetppo]

Reply

Marsh Posté le 03-03-2002 à 15:16:40    

en effet c est bien la "reference" qui faisaient planter :(
 
bon, c est corrigé, mais ca souleve un autre problème : operator= demande  un HI &hi en argument..
 
j avais redefinit l operateur + qui renvoyait un HI:
 
HI HI::operator+(HI hi);
 
et donc si j ai :
 
HI hi1,hi2,hi3;
hi1= (hi2+hi3);  ne marche pu car il faudrait que hi2+hi3 soit une reference :??:
 
c est d apres ce que me dit borland avant de compiler :
[C++ Error] main.cpp(17): E2285 Could not find a match for 'HI::operator =(HI)'
 
 VCC compile, mais me fait un plantage lors de l execution ..

Reply

Marsh Posté le 03-03-2002 à 15:21:50    

voici la fonction operator+
 
HI HI::operator+(HI &hi)  
{
 unsigned int i=0, ajout=0, retenu=0, k = 1 + (taille > hi.taille?taille:hi.taille);
 
 HI resultat(k); //construit un objet, avec nb= new char[k] et taille=k;
 
 for(i=0;i<k;i++)
 {
   ajout=retenu;  
        if(i<taille)
          ajout += nb[i];
 
   if(i<hi.taille)
  ajout += hi.nb[i];
   
   resultat.nb[i] = ajout % 256 ;
   retenu         = ajout / 256 ;
 }
 return resultat;
}

 

[jfdsdjhfuetppo]--Message édité par slvn--[/jfdsdjhfuetppo]

Reply

Marsh Posté le 03-03-2002 à 15:41:25    

slvn a écrit a écrit :

HI hi1,hi2,hi3;
hi1= (hi2+hi3);  ne marche pu car il faudrait que hi2+hi3 soit une reference :??:
 
c est d apres ce que me dit borland avant de compiler :
[C++ Error] main.cpp(17): E2285 Could not find a match for 'HI::operator =(HI)'




Ca doit pas correspondre à la ligne hi1 = hi2 + hi3 puisqu'il peut utiliser la référence du temporaire retourné par hi2.operator+(hi3)

Reply

Marsh Posté le 03-03-2002 à 17:18:39    

ce serait bien que dans ton exemple
de code tu ne passes pas d'une notation
a une autre (genre un HI qui se transforme
en HugeInt)
j'aurais du mal a croire que c'est juste du  
copier coller de ton code.
 
une bonne idee de nous repasser l'integralite
de ton code, copie-colle et sans les smileys :(.
 
L'inconvenient des operateurs surcharges
et qui est souligne dans la doc c'est que tu peux
changer completement le sens des operateurs
et donc rendre la comprehension du tout un peu delicate.
Pour eviter les problemes il faut donc essayer
le plus possible de s'en tenir a l'implantation
par defaut.
 
Quelques idees comme ca:
HI &HI::operator =(const HI&);
l'argument 1 est de type const pour eviter
qu'il ne modifie accidentellement ton objet
passe en argument. de plus il permet
d'assigner depuis des objets de type const
ce qui est primordial si ton argument n'est
pas une lvalue !! (typiquement le resultat
de a+b n'est pas une lvalue sauf si tu renvoies
une reference explicitement).
Le resultat est une reference vers l'objet *this
(important).
 
HI::HI(const HI&);
C'est l'operateur de copie. La raison
du const c'est pour la meme raison
que ci dessus. Tu ne passes pas
l'argument par valeur à l'operateur de Copie.
Dans le cas contraire il serait oblige
de s'appeler lui-meme !!
Il renvoie implicitement une reference
vers *this.
 
HI HI::operator +(const HI&) const;
Le premier const c'est pour la meme raison
que ci-dessus. Le deuxieme const c'est pour la meme raison.
(le premier argument est cache c'est *this !).
Il ne renvoie pas une reference mais un objet temporaire
const (important)!
 
HI &HI::operator +=(const HI &);
le const c'est pour la meme raison que les fois
precedentes.  
L'operateur n'est pas const
parce qu'il modifie l'objet courant
(comme l'assignement).
il ne peut donc pas operer sur des objets ou
references const!
(ex: (a+b)+=d; est illegal !)
 
A+
LEGREG

Reply

Marsh Posté le 03-03-2002 à 17:23:54    

evidemment tu PEUX t'amuser
a changer la semantique de tous
les operateurs.
Par exemple que l'operateur = renvoie un objet temp const
ou que l'operateur + renvoie une reference vers *this.
Mais gare aux prises de tete.
parce que "naturellement" tu vas
te fier au comportement "presume par defaut"
des operateurs mais que tu as change
pour des raisons X ou Y.
 
A+
LEGREG

Reply

Marsh Posté le 03-03-2002 à 17:36:45    

Pendant qu'on y est ;)
 
Par reference(const) ou par valeur?
je precise const parce que sinon y'a
meme pas de question a se poser.
Ben ca depend.
Parfois c'est mieux de passer par reference;
on s'epargne la copie de l'objet.
mais parfois c'est mieux de passer par valeur;
une fois l'objet copie on peut travailler
dessus sans se soucier de savoir qu'il est
const et sans faire de deferencement
de pointeur (implicite)  
a chaque acces a une propriete de l'objet.
 
A vous de voir
 
A+
LEGREG

Reply

Marsh Posté le 03-03-2002 à 17:46:06    

:)
en effet, ce n'est pas un copier-coller de mon code :), car je ne voulais pas decourager ceux qui voulaient m aider !
 
mais, puisque cela semble necessaire, je vais le poster  
(je le poste tout de suite, enfin apres savoir comme "eviter" de creer des smileys a cause des  ": o" )
 
en fait, ce que je cherche a recreer, c est toutes les operations normales des "int"  (+ - * ^ %) mais avec des entiers de tailles quelconques :) (que je code avec des char)
 
je cherche a faire en sorte qu il n y ait pas d instruction inutile (notement dnas les passages en parametre-reference-pointeur)
 
et qu il n y ait pas de place de perdu  
(ex : a= b * c  : si b est sur 10 octects et c sur 20 octect, alors a est sur 30 octects)

 

[jfdsdjhfuetppo]--Message édité par slvn--[/jfdsdjhfuetppo]

Reply

Marsh Posté le 03-03-2002 à 17:46:06   

Reply

Marsh Posté le 03-03-2002 à 19:12:50    

voici le bazaar :
(qui fait planter le prog sous borland a cause du delete du constructeur......)
 
(:o:o:o:o:o:o:o:o:o:o:o:o:o:o:o:o:o:o:o:o:o:o:o:o:
 
 
o:o:o:o:o:o)
 
main.cpp:
==============================================
#include "HugeInt.hpp"
#include "stdio.h"
 
int main(int argc, char * argv)
{
 
  HugeInt p("123456789" );
  HugeInt q("abcdefghijklmnopqrst" );
  HugeInt copie(q);
  HugeInt somme;
  HugeInt affectation;
 
  printf("p         : " ); p.affiche_ascii();
  printf("de taille : %i octect %i bits\n\n", p.taille, 8 * p.taille);
 
  printf("q         : " ); q.affiche_ascii();
  printf("de taille : %i octect %i bits\n\n", q.taille, 8 * q.taille);
 
  somme = q + p;
  affectation = q;
 
 
  printf("somme     : " ); somme.affiche_ascii();
  printf("de taille : %i octect %i bits\n\n", somme.taille, 8 * somme.taille);
 
  printf("copie q   : " ); copie.affiche_ascii();
  printf("de taille : %i octect %i bits\n\n", copie.taille, 8 * copie.taille);
 
  printf("affect q  : " ); affectation.affiche_ascii();
  printf("de taille : %i octect %i bits\n\n", affectation.taille, 8 * affectation.taille);
 
 
 
  //destruction
  p.~HugeInt();
  q.~HugeInt();
  somme.~HugeInt();
  affectation.~HugeInt();
 
  getchar();
  return 0;
}
==============================================
HugeInt.hpp
==============================================
//adresse basse = poids faible  
 
class HugeInt
{
 public:
 
   char * nb                      ;
   unsigned int taille            ;
 
   //constructeur-destructeur
   HugeInt(void)                  ;//grand nombre nul
   HugeInt(unsigned int taille )  ;//construit un grand nombre de 8*taille bits
   HugeInt(char * grandNombre)    ;//construit un grand nombre de chaine donnée
   HugeInt(const HugeInt& source) ;//constructeur de copie
   ~HugeInt(void)                 ;//libere la memoire occupée
 
   //methode
   void affiche_chaine(void)      ;
   void affiche_ascii(void)       ;
   
   //operation
   HugeInt operator+(HugeInt hi)  ;
   HugeInt operator=(HugeInt hi)  ;
};
==============================================
HugeIn.cpp
==============================================
#include <iostream.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include "HugeInt.hpp"
 
 
 
HugeInt::HugeInt(void)
{
  nb = NULL;
  taille = 0;
  return;
}
 
HugeInt::HugeInt(unsigned int taille)
{
  if( (HugeInt::nb=new char[taille]) == 0)
  {
    cout << "erreur allocation memoire" << endl;
    exit(0);
  }
  HugeInt::taille = taille;
  return;
}
 
 
 
HugeInt::HugeInt(char * grandNombre)
{
  unsigned int i,k;
  HugeInt::taille = k = strlen(grandNombre);
 
  if((HugeInt::nb=new char[k]) == 0)
  {
    cout << "erreur allocation memoire" << endl;
    exit(0);
  }
 
  for(i=0;i<k;i++)
 HugeInt::nb[i]=grandNombre[k -1 -i];
  return;
}
 
 
 
HugeInt::HugeInt(const HugeInt& source) //constructeur de copie
{
  unsigned int i;
  taille=source.taille;
  if((HugeInt::nb=new char[taille]) == 0)
  {
    cout << "erreur allocation memoire" << endl;
    exit(0);
  }
  for(i=0;i<taille;i++)
 nb[i]=source.nb[i];
  return;
}
 
 
//destructeur
HugeInt::~HugeInt(void)
{
 if (nb != NULL)
 {
  //delete nb;        //erreur ici  ####################################
  nb=NULL;
 }
 taille=0  ;
 return;
}
 
void HugeInt::affiche_chaine(void)  {........}
void HugeInt::affiche_ascii(void)   {.......}
 
 
 
 
HugeInt HugeInt::operator+(HugeInt hi)
{
 unsigned int i=0, ajout=0, retenu=0, k = 1 + (taille > hi.taille? taille : hi.taille);
 
 HugeInt resultat(k);
 
 for(i=0;i<k;i++)
 {
   ajout=retenu;  
          if(i < taille)
            ajout += nb[i];
 
   if(i < hi.taille)
            ajout += hi.nb[i];
   
   resultat.nb[i] = ajout % 256 ;
   retenu         = ajout / 256 ;
 }
 return resultat;
}
 
 
 
 
HugeInt HugeInt::operator=(HugeInt hi)
{  
  taille  = hi.taille;
  delete nb;
   
  if( (nb = new char[taille]) == 0)
  {
    cout << "erreur allocation memoire" << endl;
    exit(0);
  }
  memcpy(nb , hi.nb , taille);
  return *this;
}
==============================================

 

[jfdsdjhfuetppo]--Message édité par slvn--[/jfdsdjhfuetppo]

Reply

Marsh Posté le 03-03-2002 à 19:45:05    

Pourquoi appelles-tu les destructeurs toi-même ?

Reply

Marsh Posté le 03-03-2002 à 19:57:21    

je debute en C++ et j essayais de tester les trucs par moi meme :)
...je voulais savoir si on pouvais utiliser les destructeurs, en plein milieu du prog :) je crois que oui.
 
d ailleurs, apres verification, le prog du dessus marche bien avec VCC mais plante(a la fin de l execution) avec Borland  
(quand le delete du constructeur est activé)

Reply

Marsh Posté le 03-03-2002 à 19:59:29    

Certes, mais c'est dans 99% des cas une mauvaise idée car le compilo l'appelle déjà pour toi.
Et puis, comme signalé précédemment remplace delete nb par delete[] nb

Reply

Marsh Posté le 03-03-2002 à 20:02:53    

Et dans ton opérateur d'affectation, tiens compte du cas:
a = a;

 

[jfdsdjhfuetppo]--Message édité par Verdoux--[/jfdsdjhfuetppo]

Reply

Marsh Posté le 03-03-2002 à 20:06:08    

si j ai besoin de reccuperer de la memoire en plein milieu du prog, faut bien que je fasse appelle au destructeur moi meme ??
 
le delete[] ne change rien....(j aurais meme tendance a dire que delete[] == delete, enfin d apres vu sur un bouquin, ils se servait aussi bien de l un comme de l autre pour reccuperer la memoire de leur tableau)
 
sinon, bien vu pour le coup du a=a; ;)

Reply

Marsh Posté le 03-03-2002 à 23:59:18    

slvn a écrit a écrit :

si j ai besoin de reccuperer de la memoire en plein milieu du prog, faut bien que je fasse appelle au destructeur moi meme ??



 
On n'a jamais de bonne raison d'appeler le destructeur explicitement.
 
les cas qui se presentent:
- ton objet est declare en global, il est alloue
au demarrage et jamais libere jusqu'a la mort du programme.
- ton objet est sur la pile (comme dans ton main)
il est alloue a sa premiere apparition et
desalloue comme toutes les autres variables locales
a la sortie de la fonction.
- ton objet est membre d'un objet. Il est alloue
dans la memoire de ton objet (sur la pile ou sur le tas)
sa duree de vie est strictement egale a celle de l'objet.
-ton objet a ete alloue par new. Il est sur le tas
et il n'est desalloue qu'apres un appel explicite a delete.
 
Si tu veux liberer de la memoire en cours de process:
si ton objet est sur la pile tu n'as pas d'autre solution
que de le "recycler" parce que tu ne liberes pas a la main
un objet alloue sur la pile.
(ex: tu alloues un HugeInt, tu l'utilises dans une boucle
dans une partie du scope avec une certaine valeur
et dans l'autre partie du scope avec une autre valeur)
Sinon si tu veux des noms differents tu alloues
dynamiquement.
Mais bon de l'allocation dynamique qui a le scope
d'une fonction, ca s'appelle de l'allocation sur  
la pile.. (de toute facon, la plus grande
partie de la memoire allouee par ton objet
se retrouve sur le tas grace au new)
 

slvn a écrit a écrit :

le delete[] ne change rien....(j aurais meme tendance a dire que delete[] == delete, enfin d apres vu sur un bouquin, ils se servait aussi bien de l un comme de l autre pour reccuperer la memoire de leur tableau)




 
Un tableau initialise avec un new A[taille];
doit toujours etre desalloue avec delete[].
 
Pourquoi cette distinction? parce que C++ est mal
fait et ne fait pas de difference
entre le pointeur vers objet et le pointeur vers tableau.
Ainsi quand tu vas faire le delete nb;
il va appeler le destructeur pour le premier element
et pas les suivants.
 
Ok, tu vas dire qu'il n'y a pas de destructeur a appeler
sur le type char et c'est probablement la raison
pour laquelle ca va marcher mais si tu ne prends
pas cette bonne habitude ca va rapidement
etre le bordel dans ta gestion memoire
quand tu alloueras des arrays de HugeInt !
 
surtout que des petits malins peuvent prevoir
un mecanisme d'allocation et de construction
different pour les objets seuls et les arrays
et donc que appeler l'un pour l'autre
aura des effets indetermines.
 
A+
LEGREG

Reply

Marsh Posté le 04-03-2002 à 00:12:15    

slvn a écrit a écrit :

 
je cherche a faire en sorte qu il n y ait pas d instruction inutile (notement dnas les passages en parametre-reference-pointeur)




 
Peine perdue: tu n'y arriveras pas en surchargeant les operateurs.
La surcharge des operateurs c'est du sucre syntaxique,
qui permet d'appliquer a des types non naturels des operateurs dans un esprit proche de celui des types naturels.
Cela a un cout et ca s'appelle le cout d'abstraction.
parfois le cout est non negligeable.
 
Exemple: tu as trois matrices A, B et C
de taille n*n.
tu peux les additionner en une seule passe et sans objet intermediaire

Code :
  1. for (i = 0; i<n ; i++)
  2. for (j = 0; j<n ; j++)
  3. Somme(i,j)= A(i,j) + B(i,j) + C(i,j);


Sauf que pour flatter ton sens de l'ecriture
de code elegante tu vas surcharger l'operateur
addition de deux matrices.
donc tu arriveras a l'ecriture tres succincte  

Code :
  1. Matrice Somme = A + B + C;


Mais cette ecriture necessite au moins
deux matrices n*n intermediaires,
peut-etre des copies et des deferencements inutiles.
 
A+
LEGREG

Reply

Marsh Posté le 04-03-2002 à 15:53:12    

bon, donc faut pas que je me fasse d illusions quant aux performances de mes nouveaux operateurs alors :(  
 
pour les constructeurs, j avais entendu dire, qu on pouvait les utiliser en plein milieu du prog, par ex, ca facilitait le codage d une liste chainée   (lorsqu on detruit, on recolle la chaine au meme moment).
mais de toute facon, ce qui prendra le plus de place, ce n est pas les objet, mais le tableau de char, pointé par "nb", donc au pire, je ferais une fonction, qui ne servira que pour faire un "delete [] nb".
 
sinon, pile/tas, c est par rapport aux notions d asm ?? (la pile stock les variables locales, l adresse de la fonction, les parametres de la fonction,    et le tas stocke les constantes?)
 
je viens, de resoudre les pb de "plantage" sous borland, en rajoutant les parametres " ......(const HugeInt&) const"
..etant donné que j ai pas changé le code du prog je vois pas ce que ca a pu faire de plus, enfin, j ai peut etre une idée :
si je fait :    
 
void fonction( HugeInt a);
 
fonction(aaaaaa);
 
le prog, fait une copie locale du parametre effectif aaaaaa.
cette copie est utilisée au cours de la fonction, et est detruite a la fin -> d apres, le destructeur, il y a destruction et libereration de la zone memoire pointée par les pointeurs de la copie de aaaaaa.  et vu que les pointeurs de la copie ont meme valeur que les pointeurs de aaaaaa, alors le destructeurs va libere la memoire de aaaaaaa aussi ?
 
donc en utilisant une reference, y a pas destructions, donc pas d erreur:??:
 
c pas facil d etre clair lol

Reply

Sujets relatifs:

Leave a Replay

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