OVH Cloud OVH Cloud

[Debutant] Probleme de destruction

120 réponses
Avatar
Yoxoman
Bonjour à tous.

Je me permet de vous soumettre ce petit programme, qui représente en
gros la partie qui me pose problème d'un truc plus gros :

#include <iostream>

using namespace std;

class Vecteur
{
public:
double *val;
int taille;

Vecteur() {}
Vecteur(int i);
~Vecteur() {cout << "detruit" << endl; delete[] val;}

Vecteur foisDeux();
Vecteur &operator=(const Vecteur &v);
};

Vecteur::Vecteur(int i)
{
taille=i;
val=new double[i];
}

Vecteur Vecteur::foisDeux()
{
Vecteur temp(taille);

for (int i=0; i<taille; i++)
{
temp.val[i]=2*val[i];
}

return temp; //(1)
}

Vecteur &Vecteur::operator=(const Vecteur &v)
{
taille=v.taille;
val=new double[taille];

for (int i=0; i<taille; i++)
{
val[i]=v.val[i];
}

return *this;
}

int main(void)
{
Vecteur v1(3);
Vecteur v2;

v2=v1.foisDeux(); //(2)

return 0;
}

Segmentation fault.
J'ai tout d'abord eu du mal à identifier le problème, mais j'ai réussi
grâce à des supers pouvoirs hérités de Goldorak.
En fait, à la fin de la fonction foisDeux(), la variable temporaire
temp est détruite. Logique. En même temps, la fonction renvoie une
copie de l'objet, qui possède donc dans ses attributs un pointeur sur
une zone mémoire inaccessible (action du delete[]). Problème donc à la
destruction de ce dernier.

En fait, je ne sais pas trop comment arranger ce problème. Je ne
voudrais pas modifier *directement* les attributs de v1 dans la
fonction foisDeux(), pour garder ce vecteur en stock.

Merci d'avance pour votre aide.

10 réponses

1 2 3 4 5
Avatar
Olivier Azeau
Yoxoman wrote:
Bonjour à tous.

Je me permet de vous soumettre ce petit programme, qui représente en
gros la partie qui me pose problème d'un truc plus gros :

#include <iostream>

using namespace std;

class Vecteur
{
public:
double *val;
int taille;

Vecteur() {}
Vecteur(int i);
~Vecteur() {cout << "detruit" << endl; delete[] val;}

Vecteur foisDeux();
Vecteur &operator=(const Vecteur &v);
};

Vecteur::Vecteur(int i)
{
taille=i;
val=new double[i];
}

Vecteur Vecteur::foisDeux()
{
Vecteur temp(taille);

for (int i=0; i<taille; i++)
{
temp.val[i]=2*val[i];
}

return temp; //(1)
}

Vecteur &Vecteur::operator=(const Vecteur &v)
{
taille=v.taille;
val=new double[taille];

for (int i=0; i<taille; i++)
{
val[i]=v.val[i];
}

return *this;
}

int main(void)
{
Vecteur v1(3);
Vecteur v2;

v2=v1.foisDeux(); //(2)

return 0;
}

Segmentation fault.
J'ai tout d'abord eu du mal à identifier le problème, mais j'ai réussi
grâce à des supers pouvoirs hérités de Goldorak.
En fait, à la fin de la fonction foisDeux(), la variable temporaire
temp est détruite. Logique. En même temps, la fonction renvoie une
copie de l'objet, qui possède donc dans ses attributs un pointeur sur
une zone mémoire inaccessible (action du delete[]). Problème donc à la
destruction de ce dernier.

En fait, je ne sais pas trop comment arranger ce problème. Je ne
voudrais pas modifier *directement* les attributs de v1 dans la
fonction foisDeux(), pour garder ce vecteur en stock.

Merci d'avance pour votre aide.


Le pointeur présent dans la copie est rendu inaccessible car il manque
un constructeur par copie "Vecteur(const Vecteur &v)" pour réaliser une
copie en profondeur correcte.

Un autre truc en passant : il y a de la fuite mémoire dans l'opérateur
d'affectation car la zone mémoire courante n'est pas détruite.

Avatar
Gabriel Dos Reis
Yoxoman writes:

| Bonjour à tous.
|
| Je me permet de vous soumettre ce petit programme, qui représente en
| gros la partie qui me pose problème d'un truc plus gros :

Si tu es débutant, comme tu l'indiques dans le sujet, je te suggèrrerais
de laisser de côté la gestion manuelle d'allocation dynamique et d'y
revenir bien plus tard, quand tu as compris certaines notions
fondamentales de C++. En particuloer, je te suggèrerais d'utiliser le
type standard std:vector<int>.

| #include <iostream>

Ajoute

#include <vector>

|
| using namespace std;
|
| class Vecteur
| {
| public:
| double *val;
| int taille;

Remplace dces deux déclarations par :

std::vector<int> val;

|
| Vecteur() {}
| Vecteur(int i);

Remplace des deux déclarations par

explicit Vecteur(int n = 0) : val(n) { }

| ~Vecteur() {cout << "detruit" << endl; delete[] val;}

Supprime cette déclaration.

|
| Vecteur foisDeux();
| Vecteur &operator=(const Vecteur &v);

Supprime aussi la déclaration de l'opérateur d'affection et de copie.

| };
|
| Vecteur::Vecteur(int i)
| {
| taille=i;
| val=new double[i];
| }

Tu n'as plus besoin de ceci

|
| Vecteur Vecteur::foisDeux()
| {
| Vecteur temp(taille);
|
| for (int i=0; i<taille; i++)
| {
| temp.val[i]=2*val[i];
| }
|
| return temp; //(1)
| }
|
| Vecteur &Vecteur::operator=(const Vecteur &v)
| {
| taille=v.taille;
| val=new double[taille];
|
| for (int i=0; i<taille; i++)
| {
| val[i]=v.val[i];
| }
|
| return *this;
| }

Tu n'as plus besoin de cette fonction non plus.

|
| int main(void)
| {
| Vecteur v1(3);
| Vecteur v2;
|
| v2=v1.foisDeux(); //(2)
|
| return 0;
| }
|
| Segmentation fault.
| J'ai tout d'abord eu du mal à identifier le problème, mais j'ai réussi
| grâce à des supers pouvoirs hérités de Goldorak.
| En fait, à la fin de la fonction foisDeux(), la variable temporaire
| temp est détruite. Logique. En même temps, la fonction renvoie une
| copie de l'objet, qui possède donc dans ses attributs un pointeur sur
| une zone mémoire inaccessible (action du delete[]). Problème donc à la
| destruction de ce dernier.

Oui.

|
| En fait, je ne sais pas trop comment arranger ce problème. Je ne
| voudrais pas modifier *directement* les attributs de v1 dans la
| fonction foisDeux(), pour garder ce vecteur en stock.

Laisse tomber la gestion manuelle de mémoire pour l'instant ;
concentre toi sur le vrai problème à résoudre.

-- gaby
Avatar
Falk Tannhäuser
Yoxoman wrote:

int main(void)
{
Vecteur v1(3);
Vecteur v2;

v2=v1.foisDeux(); //(2)

return 0;
}


Si tu n'as pas de raison particulière de réinventer la roue
(exercice ?), utilise plutôt les types standard, afin de ne
pas t'embêter avec la gestion manuelle de la mémoire !
Gaby t'a déjà proposé 'std::vector'. Une autre possibilité
pourrait être l'utilisation de 'std::valarray' - cela dépend
de ce que tu veux faire avec... Par exemple, ton programme
pourrait être réécrit ainsi :

#include <valarray>
#include <iostream>
#include <ostream>

int main()
{
std::valarray<int> v1(3);
// ... remplissage de v1
std::valarray<int> v2 = v1 * 2;

for(unsigned i=0; i<v2.size(); ++i)
std::cout << ' ' << v2[i];
std::cout << 'n';

return 0;
}


Falk

Avatar
Olivier Azeau
Gabriel Dos Reis wrote:

Laisse tomber la gestion manuelle de mémoire pour l'instant ;
concentre toi sur le vrai problème à résoudre.



Tout dépend du contexte...
Si le problème à résoudre n'est qu'un prétexte à l'apprentissage du C++,
j'estime qu'il n'y a pas de plus mauvais conseil possible...

Avatar
Gabriel Dos Reis
Olivier Azeau writes:

| Gabriel Dos Reis wrote:
| > Laisse tomber la gestion manuelle de mémoire pour l'instant ;
| > concentre toi sur le vrai problème à résoudre.
| >
|
| Tout dépend du contexte...
| Si le problème à résoudre n'est qu'un prétexte à l'apprentissage du
| C++, j'estime qu'il n'y a pas de plus mauvais conseil possible...

Le plus mauvais conseil possible, je crois, c'est de lancer un
débutant dans la gestion manuelle de new/delete alors qu'il n'a pas
encore appris les bases.

-- Gaby
Avatar
Olivier Azeau
Gabriel Dos Reis wrote:
Olivier Azeau writes:

| Gabriel Dos Reis wrote:
| > Laisse tomber la gestion manuelle de mémoire pour l'instant ;
| > concentre toi sur le vrai problème à résoudre.
| >
|
| Tout dépend du contexte...
| Si le problème à résoudre n'est qu'un prétexte à l'apprentissage du
| C++, j'estime qu'il n'y a pas de plus mauvais conseil possible...

Le plus mauvais conseil possible, je crois, c'est de lancer un
débutant dans la gestion manuelle de new/delete alors qu'il n'a pas
encore appris les bases.

-- Gaby


Apprendre à se servir des templates et de la STL ? C'est ça les bases ?
Et quand, par malheur, on a un problème, on rentre à coup de debugger
dans les Allocators pour voir ce qui s'y passe ?

Moi qui croyais que les bases du C++ c'était la structure fondamentale
d'une classe avec son constructeur par défaut, son constructeur par
copie, son opérateur d'affectation et son destructeur...
Moi qui croyais que c'etait aussi la maîtrise du passage d'objet par
référence/par valeur...

On m'aurait menti à l'insu de mon plein gré ?

Avatar
Gabriel Dos Reis
Olivier Azeau writes:

| Gabriel Dos Reis wrote:
| > Olivier Azeau writes:
| > | Gabriel Dos Reis wrote:
| > | > Laisse tomber la gestion manuelle de mémoire pour l'instant ;
| > | > concentre toi sur le vrai problème à résoudre.
| > | >
| > | | Tout dépend du contexte...
| > | Si le problème à résoudre n'est qu'un prétexte à l'apprentissage du
| > | C++, j'estime qu'il n'y a pas de plus mauvais conseil possible...
| > Le plus mauvais conseil possible, je crois, c'est de lancer un
| > débutant dans la gestion manuelle de new/delete alors qu'il n'a pas
| > encore appris les bases.
| > -- Gaby
|
| Apprendre à se servir des templates et de la STL ? C'est ça les bases ?

Apprendre à se servir de la bilbiothèque standard. Oui. On n'a besoin
d'aucun talent particulier des templates.

Cela n'a pas grand sens de se lancer dans la conception d'une classe,
quand on ne connaît pas les outils fondamentaux 101.
Ah, c'est vrai j'oubliais il y a encore un certain nombre de gens qui
pensent que C++ doit s'apprendre ou s'enseigner comme eux ils l'ont
appirs il y a plus d'une décennie, alors le langage d'aujourd'hui est
vachement différent. CIl ne reste plus qu'à espérer que la promotion
naturelle les sortira de la circulation, le plus rapidement possible.


-- Gaby
Avatar
Yoxoman

Le pointeur présent dans la copie est rendu inaccessible car il manque
un constructeur par copie "Vecteur(const Vecteur &v)" pour réaliser une
copie en profondeur correcte.


Oki.

Un autre truc en passant : il y a de la fuite mémoire dans l'opérateur
d'affectation car la zone mémoire courante n'est pas détruite.


Heu... Je ne vois pas. Le destructeur ne détruit pas tout?

Avatar
Yoxoman

Si tu es débutant, comme tu l'indiques dans le sujet, je te suggèrrerais
de laisser de côté la gestion manuelle d'allocation dynamique et d'y
revenir bien plus tard, quand tu as compris certaines notions
fondamentales de C++. En particuloer, je te suggèrerais d'utiliser le
type standard std:vector<int>.


Oki. J'ai tout de même une question stupide : y a-t-il une classe
équivalente pour les matrices ?
Sinon, je me doute qu'on peut utiliser std::vector avec une taille de
nbLignes*nbColonnes.

Laisse tomber la gestion manuelle de mémoire pour l'instant ;
concentre toi sur le vrai problème à résoudre.


Tu as peut-être raison, mais je vais quand même finir ce que j'ai
commencé.

Avatar
Olivier Azeau
Yoxoman wrote:
Un autre truc en passant : il y a de la fuite mémoire dans l'opérateur
d'affectation car la zone mémoire courante n'est pas détruite.



Heu... Je ne vois pas. Le destructeur ne détruit pas tout?



Vecteur &Vecteur::operator=(const Vecteur &v)
{
taille=v.taille;
val=new double[taille];

for (int i=0; i<taille; i++)
{
val[i]=v.val[i];
}

return *this;
}

Aucun destructeur n'est appelé quand on fait "Vecteur x,y; x = y;"

Lors du "x=y" le x.val reçoit un "new double[taille]" qui écrase le
x.val alloué dans le constructeur par défaut sans faire le "delete []"
correspondant.


1 2 3 4 5