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

8 9 10 11 12
Avatar
Marc Boyer
wrote:
Olivier Azeau wrote:
wrote:
Olivier Azeau wrote:



[SNIP]

Si la bibliothèque standard couvrait tout ce qu'on peut faire
avec des pointeurs, et qu'on n'avait pas réelement besoin des
pointeurs
dans la programmation applicative, je dirais qu'on pourrait se passer
d'enseigner des pointeurs. Ce n'est pas le cas pour les pointeurs, mais
c'est peut-être le cas pour certaines autres techniques avancées.


J'ajouterais que les pointeurs sont peut-être simplement l'exemple
le plus courant d'identifiant sur ressource à obtenir, dont
l'obtention peut échouer, qui peuvent être partagés et qu'on doit
libérer.

Marc Boyer
--
Je ne respecte plus le code de la route à vélo depuis une double fracture
due au fait que j'étais le seul à le respecter.



Avatar
kanze
Olivier Azeau wrote:

[...]
Je vois pour ma part des personnes qui connaissent des langages
évolués, dont le C++, et qui savent faire des bons trucs mais,
quand

on gratte la surface pour aller sur un comportement plus bas niveau,
il n'y a plus personne.


Et alors ?

Le tout dépend des buts de l'enseignement, et ce que l'élève compte
en
faire. Si le but est d'utiliser l'informatique comme un outil dans un
autre métier, et que le langage choisi est le C++, je ne vois pas
l'intérêt à ce qu'ils passent énormement de temps à apprendre des
subtilités de bas niveau -- qu'il sache ce que c'est qu'un pointeur,
et
l'indirection, d'accord, parce que c'est une nécessité fondamentale,
mais qu'il maîtrise l'arithmétique sur pointeurs... Je n'en vois pas
l'intérêt -- il y a des choses plus importantes à apprendre.

Je ne critique pas telle ou telle approche éducative : je dis juste
que, pour *mon contexte*, les pointeurs *c'est la base* et que celui
qui ne maîtrise pas ça, il ne maîtrise rien.


Quel est « ton contexte » ? Parce que moi, je me suis pas servi de
l'arithmétique sur pointeurs dans la vie professionnelle depuis au
moins
dix ans. Et je n'aurais pas de problème avec un programmeur C++ qui ne
savait même pas que ça existait.

Dans ma vie non professionnelle, la programmation bas niveau
m'intéresse, et je me suis bien servi de l'arithmétique des pointeurs
dans des choses que j'ai implémenté pour moi-même. De même, je
crois que
quelqu'un qui vise le metier d'ingénieur en informatique doit bien
voir
un peu les bases de l'informatique, un jour ou l'autre dans son
curriculum. Mais même pour eux, je dirais que ce n'est pas nécessaire
en
informatique 101.

C'est pour cela que j'ai, légitimement je pense, un peu "peur" quand
on veut faire passer le C++ pour un langage de haut-niveau : dans
beaucoup de contextes, même si on essaie de faire évoluer les
choses,

cela n'est pas la réalité quotidienne.


C'est la réalité dans les boîtes où j'ai travaillé -- le « bas
niveau »
qui nous a intéressé n'était jamais celui de C++, mais des sockets,
les
threads, et des bases de données. Et même là, il y avait toujours un
petit groupe qui s'y connaissaient, et qui encapsulait pour que les
autres n'avaient pas à y connaître. Mais ça dépend du domaine de
l'application, évidemment.

J'ai l'impression qu'avec une telle approche on va rapidement en
arriver (si ce n'est déjà fait) à commencer par enseigner Loki
pour

découvrir des patterns fabrique ou visiteur sous prétexte que c'est
plus haut niveau et que l'on pourra voir les détails ensuite... Et


il y aura des "vieux réacs" pour avoir l'impression que l'on apprend
à

qqun à conduire une voiture en le mettant aux commandes d'une
Formule

1.


La comparaison qui me vient à l'esprit, c'est apprendre à règler un
carburateur pour apprendre à conduire. Mon frère (qui est expert en
méchanique) sait règler un carburateur. Il sait probablement aussi
conduire mieux que moi. Mais la différence ne se fait pas sentir dans
l'utilisation que je fais de la conduite.

La même chose vaut pour la programmation. Si on connaît les détails
du
bas niveau, c'est fort possible qu'on saurait mieux programmer. Mais la
différence ne se ferait pas sentir si on ne travaille que sur des
applications ; si on veut travailler sur des projets comme celui où je
travaille actuellement, et la question se pose où investir le temps
d'apprentissage, des choses comme l'UML, les bases de données, et
même
la législation boursière me semble plus important que l'arithmétique
des
pointeurs. Y compris pour écrire du C++.

--
James Kanze GABI Software http://www.gabi-soft.fr
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34

Avatar
Matthieu Moy
Olivier Azeau writes:

Je vois pour ma part des personnes qui connaissent des langages
évolués, dont le C++, et qui savent faire des bons trucs mais, quand
on gratte la surface pour aller sur un comportement plus bas niveau,
il n'y a plus personne.


Je te rassure, la réciproque est vraie aussi, surtout avec le C++
qu'un programmeur C peut prétendre connaitre s'il sait faire "mv
toto.c toto.cpp" ... Je pourrais citer l'exemple (vecu) de quelqu'un
faisant du C++ depuis 3 ans pour son boulot, et qui vient de découvrir
std::vector !

--
Matthieu

Avatar
Gabriel Dos Reis
Olivier Azeau writes:

[...]

| J'ai l'impression qu'avec une telle approche on va rapidement en
| arriver (si ce n'est déjà fait) à commencer par enseigner Loki pour

enseigner quoi ?

Mes excuse, je n'ai pas pu resister.

| découvrir des patterns fabrique ou visiteur sous prétexte que c'est
| plus haut niveau et que l'on pourra voir les détails ensuite...
| Et là il y aura des "vieux réacs" pour avoir l'impression que l'on
| apprend à qqun à conduire une voiture en le mettant aux commandes
| d'une Formule 1.

Je ne suis pas réact, ni vieux, mais là, excuse-moi -- mes respects
pour Andrei et toussa -- mais c'est de l'abus :-)

-- Gaby
Avatar
Alain Naigeon
"Gabriel Dos Reis" a écrit dans le message
news:

Pardon ?


Ben, t'as un clone ici, alors...

--

Français *==> "Musique renaissance" <==* English
midi - facsimiles - ligatures - mensuration
http://anaigeon.free.fr | http://www.medieval.org/emfaq/anaigeon/
Alain Naigeon - - Strasbourg, France

Avatar
Alain Naigeon
"Loïc Joly" a écrit dans le message news:
41be9c49$0$11726$
Olivier Azeau wrote:

C'est exactement ce qui me gêne : "les apprendre plus tard".
Si j'apprends avec un radar de recul, qu'est ce qui va me motiver pour
*ensuite* apprendre sans ?
Pareil pour les pointeurs : personne ne me fera écrire une classe string
à la main si qqun d'autre m'a déja montré std::string


Non, et c'est très bien. Par contre, quand on te montrera comment
utiliser un schmurtzHandle* manuellement, tu n'auras qu'une envie :
Créer une classe Schmurtz, qui sera à un schmurtzHandle* ce que
std::string est à char *.

Et donc tu apprendras à faire ce genre de classe, mais en plus, tu auras
un exemple pour te guider dans tes choix.


Ces exemples ont toujours existé dans les bons livres ; on te guidait,
on te montrait même plusieurs stratégies parfois, en expliquant les
avantages et inconvénients. D'un point de vue pédagogique, auquel
tu te réfères ici, c'était même mieux que de recevoir un truc tout fait
qui ne se discute pas.

--

Français *==> "Musique renaissance" <==* English
midi - facsimiles - ligatures - mensuration
http://anaigeon.free.fr | http://www.medieval.org/emfaq/anaigeon/
Alain Naigeon - - Strasbourg, France


Avatar
Gabriel Dos Reis
"Alain Naigeon" writes:

| "Gabriel Dos Reis" a écrit dans le message
| news:
|
| > Pardon ?
|
| Ben, t'as un clone ici, alors...

Oooh ho.

-- Gaby
Avatar
kanze
Luc Hermitte wrote:
wrote in news:1103017263.964491.104460
@f14g2000cwb.googlegroups.com:

<petite parenthèse>

Si on est concerné par la question de la vitesse, AMHA, la
première chose à faire serait de supprimer l'allocation si la
destination a déjà la bonne taille, c-à-d quelque chose du
genre




:

double * newval =
( taille == other.taille ? val : new double[ other.taille ] )
;




std::copy( other.val, other.val + other.taille, newval ) ;
if ( newval != val ) delete [] val ;
val = newval ;
taille = other.taille ; return *this ;


Dans ce cas particulier où l'on gère des doubles c'est OK. Dans
le



cas plus générique où l'on pourrait avoir un vecteur de
n'importe



quoi, et en particulier des choses dont les copies peuvent lever
des exceptions, cette méthode n'est plus bonne dans la mesure où
elle invaliderait l'objet que l'on tentait de modifer, si une
exception apparait durant le std::copy.


Ah bon. Il me semble que si l'opérateur d'affectation de l'object
est lui-même exception-safe, le tableau restera valid. On ne
saurait


pas exactement ce qu'il contient, mais typiquement, ça ne fait
rien,


parce qu'il serait de toute façon detruite lors du nettoyage de la
pile.


Si il est exception-safe (comme avec les doubles), c'est OK. Mais
s'il

n'est qu'exception-neutral (l'état de l'objet reste inmodifié, mais
l'exception est bien levée), alors les premiers éléments peuvent
être

bien copiés, mais pas les derniers.


Et alors ?

Tout dépend de l'utilisation, qui elle dépend de la sémantique. Dans
la
pratique, je constate que les collections dans une application tombe
dans deux catégories :
- les « temporaires », qui ne sert que dans une seule requête (par
exemple, pour en collecter le résultat), et
- celles de la base de données (au sens large) qui sont toujours
présentes.
Pour les premières, l'exception va en provoquer la destruction. Alors,
qu'importe le contenu en cas d'exception. Et pour les deuxièmes, la
cohérence réquise dépasse le niveau d'une opération seule ; on est
donc
obligé de toute façon de prévoir d'autres moyens. (En fait, dans le
deuxième cas, les collections contiennent pratiquement toujours que
des
pointeurs. Dont la copie ne peut pas provoquer une exception.)

Et en passant, je me pose des questions en ce qui concerne ta
terminologie. En anglais, « exception safe » n'a pas de définition
bien
établie, mais en général, il réfère à la garantie que fait
l'objet
vis-à-vis d'une exception en provenance d'une fonction externe à
l'objet
(opération sur un sous-objet ou un élément, fonction libre, etc.)
Dans
ce sens, parler d'« exception safe » en ce qui concerne un double n'a
pas de sens, parce qu'il n'a pas de sous-objet ni d'opérations qui
font
appelle à des fonctions libres. On dirait plutôt qu'un double est
« no-throw », c-à-d qu'aucune opération sur un double peu lever une
exception.

L'expression « exception safe » s'applique en revanche à la
collection. Que se passe-t-il si une opération sur un élément lève
une
exception ? Si on n'a pas défini un contrat dans ce cas-là, la
collection n'est pas exception-safe. Si on l'a défini, ça dépend du
contrat et des auteurs -- moi-même, je me range avec le grand
spécialiste des exceptions, David Abrahams, pour dire que si le
comportement est bien spécifier, et que la collection reste
destructible, elle est « exception-safe ». Abrahams a décerné un
certain
nombres de garanties diverses, et la garantie forte (dont tu sembles
parler) exige souvent beaucoup d'effort (et coût en temps d'execution)
pour rien.

Au niveau mémoire il n'y a pas de problème. Côté cohérence de la
donnée, je ne suis pas persuadé que l'on peut toujours être sûr
que ce

n'est pas grâve.


Tout dépend de l'utilisation. La plupart du temps, au moins dans mes
applications, ça ne fait rien. Parmi mes collections, j'ai :

- Les données de l'application. Les objets dans les collections sont
des objets entité, souvent polymorphique, prèsque toujours avec
une
identité, et qui ne supportent pour ainsi dire jamais
l'affectation.
Les collections contiennent des pointeurs, et donc, toutes les
opérations sur les éléments de la collection sont no-throw.

Ça ne veut pas dire que le problème ne se pose pas. Si je modifie
un
élément de la collection, et qu'il y a une exception plus tard,
avant que j'ai tout fini, il faut bien que je restitue l'élément
à
son état précédant. Mais ce n'est pas en évitant de renvoyer
une
valeur quand je modifie la collection qui va changer quelque chose
ici.

Ceci dit, il faut bien une garantie forte pour certaines
opérations
de base ici -- si j'ai un bad_alloc suite à une insertion, il faut
bien que la collection ait son contenu d'avant l'insertion. Mais
dans la mésure où les pointeurs ne peuvent pas lever les
exceptions,
ce n'est que lors des allocations de mémoire que la problème se
pose.

- Les données propres à chaque requête (ou chaque opération de
base,
selon l'application). Ici, pas besoin d'une garantie forte, parce
qu'une exception va provoquer l'abandon de la requête, et la
destruction de la collection. (En passant, toutes les instances
d'une pile que j'ai eu se trouve dans cette catégorie.)

- Quelque collections globales quasi immutables, du genre
Configuration (typiquement, un map<string,string> à la base).
Ici,
toutes les modifications dans la collection ont lieu au démarrage
du
programme, et en cas d'exception, le programme s'avorte.

J'imagine que dans d'autres types d'applications, d'autres utilisations
se présentent. Dans une IHM graphique, par exemple, les collections
des
composants contenu dans un composant (les boutons dans un carreau, par
exemple) a bien besoin de la garantie forte. (Mais là aussi, le
contenu
a une identité et est polymorphique, et la collection contient des
pointeurs.)

Je ne vois pas ce qui garantit, toujours, le nétoyage de la pile en
fait


La norme C++.

Il y a toujours moyen de trouver une situation où la donnée
(tableau)

que l'on veut modifier (par affectation) a une durée de vie qui
englobe le bloc try-catch qui va traiter l'exception.


Certes. Mais en ce qui concerne ces cas-là, au moins dans mes
applications, soit ils aboutissent à l'abandon du programme, soit la
collection ne contient que les pointeurs, dont la copie est no-throw.

Bref, cela dépend clairement de comment on veut l'utiliser. Dans
l'absolu, ce n'est pas systématiquement utilisable dans une solution
générique. (ce n'est bien qu'une parenthèse)


Mais dans la pratique, chercher à résoudre les problèmes que tu n'as
pas, c'est du temps et de l'argent jeté à la poubelle.

--
James Kanze GABI Software http://www.gabi-soft.fr
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34




Avatar
kanze
Olivier Azeau wrote:
wrote:
Olivier Azeau wrote:



[...]

- Apprendre d'abord à faire des créneaux avec le radar de recul,
puis ensuite sans, ne serait peut-être pas une mauvaise idée.
Si


on ne l'a pas essayée encore, c'est sans doute que les radar de
recul sont encore assez rare. Il n'est pas question que les
élèves


n'apprenent jamais des pointeurs. Seulement qu'ils les apprenent
plus tard, quand ils ont déjà maîtriser des choses plus
simples.



C'est exactement ce qui me gêne : "les apprendre plus tard". Si
j'apprends avec un radar de recul, qu'est ce qui va me motiver pour
*ensuite* apprendre sans ?


Le fait que tu n'auraa pas ton permis sinon ? Le fait que tu saches que
ta voiture future ne l'aura pas. Et le jours où toutes les voitures
auront un radar de récul, est-ce qu'il faudrait encore apprendre à
faire
des crénaux sans ?

Pareil pour les pointeurs : personne ne me fera écrire une classe
string à la main si qqun d'autre m'a déja montré std::string


Sauf si tu as besoin d'autre chose. À vrai dire, je ne sais pas
pourquoi
quelqu'un aujourd'hui écrirait une classe String en C++. En fait, je
ne
vois pas

J'ajouterai que cette philosophie n'est pas vraiment une
innovation,


sauf dans la mésure qu'elle s'applique au C++ (et encore -- j'ai
vu


de l'enseignement de C++ il y a 15 ans qui se servait des classes
de


String et d'Array « maison » pour éviter d'avoir à aborder les
pointeurs trop tôt). C'est en fait l'ordre classique dans d'autres
langages, comme Pascal. (Voir l'ordre de présentation de Wirth,
par


exemple.) Je dirais qu'on enseigne le C++ comme ça, c'est une
signe


d'une certaine maturité.


De quelle "maturité" parle-t-on ?


De pouvoir utiliser les abstractions.

Celle où on forme des gens au C++ en leur apprenant à faire un
paquet

de trucs sans pointeurs, pointeurs qu'un certain nombre d'entre eux
ne

maîtriseront jamais ?


Attention. Les pointeurs, tout le monde va les voir, parce qu'il va les
falloir au plus tard quand on s'attaque aux algorithmes de graphes et
d'autres. Voir même quand on s'attaque à la polymorphisme, des
fonctions
virtuelles, des objets à identité qui ne supportent pas la copie,
etc.
Je n'ai jamais vu une application C++ qui s'en passait, et je n'ai
jamais vu un programmeur, quelque soit sa formation (y compris un qui
n'a été formé qu'en Pascal), qui avait le moindre problème avec ces
utilisations des pointeurs. Ce n'est pas la première chose qu'on
abordera -- pour commencer, prèsque toutes ses utilisations de
pointeur
(à part les graphes) mettent les pointeurs dans une collection ; ce
qui
veut dire qu'il faut connaître std::vector (ou d'autre) pour
comprendre
l'utilité des pointeurs.

En C (et en C++), il y a aussi l'arithmétique sur pointeurs -- le
pointeur en C ou en C++, c'est vraiment le pointeur au niveau hardware.
Et souvent, quand on parle d'enseigner les pointeurs, c'est de ça
qu'on
parle. Or, ça, c'est bon si on fait un cours sur la programmation de
bas
niveau (ce qu'on doit faire si on veut devenir ingenieur en
informatique, disons à la troisième ou à la quatrième année). Mais
personnellement, je n'en vois pas l'intérêt dans un cours « C++ »,
où le
seul but est d'apprendre aux gens d'écrire des programmes. Pourquoi
enseigner quelque chose qui ne doit jamais servir à la plupart des
élèves ?

--
James Kanze GABI Software http://www.gabi-soft.fr
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34


Avatar
Olivier Azeau
wrote:
En C (et en C++), il y a aussi l'arithmétique sur pointeurs -- le
pointeur en C ou en C++, c'est vraiment le pointeur au niveau hardware.
Et souvent, quand on parle d'enseigner les pointeurs, c'est de ça
qu'on
parle. Or, ça, c'est bon si on fait un cours sur la programmation de
bas
niveau (ce qu'on doit faire si on veut devenir ingenieur en
informatique, disons à la troisième ou à la quatrième année). Mais
personnellement, je n'en vois pas l'intérêt dans un cours « C++ »,
où le
seul but est d'apprendre aux gens d'écrire des programmes. Pourquoi
enseigner quelque chose qui ne doit jamais servir à la plupart des
élèves ?



En fait je crois que c'est là le fond du débat :

Pourquoi enseigner le C++ à des gens qui ne feront de ttes façons que
des programmes qu'ils feraient de manière beaucoup plus efficace avec un
autre langage ?
A moins de prendre le C++ comme une matière pointue et sélective dans le
cadre de l'attribution d'un diplôme (un peu comme les maths au lycée),
je ne vois pas.

8 9 10 11 12