OVH Cloud OVH Cloud

Destructeur scalaire

13 réponses
Avatar
superbabar51
Bonjour.

Lors de la destruction de la classe que vous trouverez ci-dessus, je me
retrouve avec une erreur de manipulation de m=E9moire. VC++7 renvoie:
HEAP[prog.exe]: Heap block at 088B0788 modified at 088B07B5 past
requested size of 25
Une inspection du call stack informe que le pb se produit lors d'un
delete emit par le "scalar destructing constructor".
Une id=E9e?

Pour info, la classe est allou=E9 ainsi:
CBSP *map =3D new("afile.bsp");
Et est detruite explicitement par
if(map)
delete map

class CBSP
{
public:
CBSP(string sFileName);
~CBSP();
void Load();
private:
/* fonctions */
string m_sFileName;
HANDLE m_hMapFile;
};

CBSP::~CBSP()
{
if(m_hMapFile)
{
CloseHandle(m_hMapFile);
m_hMapFile =3D NULL;
}
}

Merci!

10 réponses

1 2
Avatar
Patrick 'Zener' Brunet
Bonjour.

Je réponds à qui a écrit
:
Bonjour.

Lors de la destruction de la classe que vous trouverez ci-dessus, je
me retrouve avec une erreur de manipulation de mémoire. VC++7 renvoie:
HEAP[prog.exe]: Heap block at 088B0788 modified at 088B07B5 past
requested size of 25
Une inspection du call stack informe que le pb se produit lors d'un
delete emit par le "scalar destructing constructor".
Une idée?

Pour info, la classe est alloué ainsi:
CBSP *map = new("afile.bsp");


Il fait quoi ce new ? Vous avez essayé de rentrer dedans au debugger
(jusqu'au constructeur) ?
S'il essaie de construire (quoi au juste ?) sur l'espace correspondant à la
chaîne constante, ça ne doit pas être triste... Et en tout cas le delete
standard ne s'applique pas dans ce contexte.

Vosu vouliez problablement écrire :

CBSP * map = new CBSP( "afile.bsp");

Cordialement,

--
/***************************************
* Patrick BRUNET
* E-mail: lien sur http://zener131.free.fr/ContactMe
***************************************/

Avatar
Christophe de Vienne
wrote:
[...]
Pour info, la classe est alloué ainsi:
CBSP *map = new("afile.bsp");


Le problème vient, je suppose, de là.
Il faut écrire, pour instancier la classe CBSP :

CBSP *map = new CBSP("afile.bsp");


A+

Christophe

Avatar
superbabar51
Oups.. Je suis allé un peu vite en écrivant le message.
Le code est bien CBSP *map = new CBSP("afile.bsp").

Par contre, j'ai remarqué que le pb n'apparaissait pas si je ne
déclanchais pas d'exception dans le code. Il me manque probablement de
la théorie sur les exceptions.

La classe envoyé est EBSPLoad, définie comme hérité de Exception:
class Exception /* Une classe de base */
{
public:
Exception(const char* lpMessage, char* lpFileName, int iLineNb);
Exception(DWORD dwError, char* lpFileName, int iLineNb);
void Display();
protected:
string m_sMessage;
string m_sFileName;
int m_iLineNb;
};

class EBSPLoad : public Exception
{
public:
EBSPLoad(const char* lpMessage, char* lpFileName, int iLineNb) :
Exception(lpMessage, lpFileName, iLineNb) {};
EBSPLoad(DWORD dwError, char* lpFileName, int iLineNb) :
Exception(dwError, lpFileName, iLineNb) {};
};

Quel est le piège à c.. pour débutant (c'es tt comme) que je ne vois
pas ??
Avatar
superbabar51
Oups.. Le code est bien
CBSP *map = new CBSP("afile.bsp");
(juste une faute de copie).

Par contre, j'ai remarqué que le pb n'apparaissait que lorsqu'une
exception CBSPLoad (cf plus bas) est déclanchée. Il doit se passer un
truc au niveau des constructeurs de classes dérivées auquel je ne
pense pas..


class Exception /*classe de base */
{
public:
Exception(const char* lpMessage, char* lpFileName, int iLineNb);
Exception(DWORD dwError, char* lpFileName, int iLineNb);
void Display();
protected:
string m_sMessage;
string m_sFileName;
int m_iLineNb;
};

class EBSPLoad : public Exception
{
public:
EBSPLoad(const char* lpMessage, char* lpFileName, int iLineNb) :
Exception(lpMessage, lpFileName, iLineNb) {};
EBSPLoad(DWORD dwError, char* lpFileName, int iLineNb) :
Exception(dwError, lpFileName, iLineNb) {};
};

Le tout sous VC++7
using std::string;

handler d'exception:
try {
map->Load();
}
catch(EBSPLoad bl)
{
bl.Display();
}
if(map)
delete map;
Avatar
Patrick 'Zener' Brunet
Bonjour.

Je réponds à qui a écrit
:
Oups.. Le code est bien
CBSP *map = new CBSP("afile.bsp");
(juste une faute de copie).

Par contre, j'ai remarqué que le pb n'apparaissait que lorsqu'une
exception CBSPLoad (cf plus bas) est déclanchée. Il doit se passer un
truc au niveau des constructeurs de classes dérivées auquel je ne
pense pas..


class Exception /*classe de base */
{
public:
Exception(const char* lpMessage, char* lpFileName, int iLineNb);
Exception(DWORD dwError, char* lpFileName, int iLineNb);
void Display();
protected:
string m_sMessage;
string m_sFileName;
int m_iLineNb;
};

class EBSPLoad : public Exception
{
public:
EBSPLoad(const char* lpMessage, char* lpFileName, int iLineNb) :
Exception(lpMessage, lpFileName, iLineNb) {};
EBSPLoad(DWORD dwError, char* lpFileName, int iLineNb) :
Exception(dwError, lpFileName, iLineNb) {};
};

Le tout sous VC++7
using std::string;

handler d'exception:
try {
map->Load();
}
catch(EBSPLoad bl)
{
bl.Display();
}
if(map)
delete map;


AMHA il y a quelques gros problèmes de conception là-dedans :

1) Vous utilisez une exception qui contient elle-même des composants qui
font de l'allocation dynamique, et donc qui sont susceptible de déclencher
avec un e des exceptions : vous jouez avec le feu.
Il serait plus sage, autant que possible, de disposer d'une exception (voire
d'un petit pool) qui soit (entièrement) allouées à l'avance, de manière à ne
pas introduire d'éventualité d'échec.

2) Votre exception est transmise par copie semble-t-il, et non par adresse.
Cela implique tout un tas de cycles de création/copie/destruction de
chaînes, ce qui est d'une part très inefficace, et d'autre part remultiplie
les cas d'échec.

3) Je ne vois pas bien dans votre code où se fait la destruction de
l'exception elle-même. Il faut bien pourtant qu'elle ait lieu.

4) Pour ce qui est de sa création correcte, ça n'apparaît pas dans le code
soumis... Comment écrivez-vous les constructeurs, et l'instruction throw ?

5) Quand vous suivez votre code pas à pas au debugger (en entrant bien
partout), où exactement dans le code obtenez-vous l'erreur ?

Cordialement,

--
/***************************************
* Patrick BRUNET
* E-mail: lien sur http://zener131.free.fr/ContactMe
***************************************/

Avatar
kanze
Patrick 'Zener' Brunet wrote:

Je réponds à
Oups.. Le code est bien
CBSP *map = new CBSP("afile.bsp");
(juste une faute de copie).

Par contre, j'ai remarqué que le pb n'apparaissait que
lorsqu'une exception CBSPLoad (cf plus bas) est déclanchée.
Il doit se passer un truc au niveau des constructeurs de
classes dérivées auquel je ne pense pas..

class Exception /*classe de base */
{
public:
Exception(const char* lpMessage, char* lpFileName, int iLineNb);
Exception(DWORD dwError, char* lpFileName, int iLineNb);
void Display();
protected:
string m_sMessage;
string m_sFileName;
int m_iLineNb;
};

class EBSPLoad : public Exception
{
public:
EBSPLoad(const char* lpMessage, char* lpFileName, int iLineNb) :
Exception(lpMessage, lpFileName, iLineNb) {};
EBSPLoad(DWORD dwError, char* lpFileName, int iLineNb) :
Exception(dwError, lpFileName, iLineNb) {};
};

Le tout sous VC++7
using std::string;

handler d'exception:
try {
map->Load();
}
catch(EBSPLoad bl)
{
bl.Display();
}
if(map)
delete map;


AMHA il y a quelques gros problèmes de conception là-dedans :

1) Vous utilisez une exception qui contient elle-même des
composants qui font de l'allocation dynamique, et donc qui
sont susceptible de déclencher avec un e des exceptions : vous
jouez avec le feu. Il serait plus sage, autant que possible,
de disposer d'une exception (voire d'un petit pool) qui soit
(entièrement) allouées à l'avance, de manière à ne pas
introduire d'éventualité d'échec.


Oui et non. Tout dépend de l'application -- souvent, on peut
raisonablement supposer qu'on n'épuisera pas la mémoire, et
aborter si on le fait (d'autant plus que souvent, on n'a pas le
choix -- le système d'exploitation le fait pour nous). Alors, on
positionne le new_handler, et on s'occupe d'autres problèmes.

Sinon, il faut certainement des classes faites sur mésure dans
l'exception, pour gerer des chaînes sans allocation dynamique.

2) Votre exception est transmise par copie semble-t-il, et non
par adresse.


Toute exception est transmise par copie. Il le faut bien,
puisqu'on va déballer la pile, et la mémoire où l'utilisateur le
construit (comme temporaire) va disparaître.

Cela implique tout un tas de cycles de
création/copie/destruction de chaînes, ce qui est d'une part
très inefficace, et d'autre part remultiplie les cas d'échec.


Ça implique *une* copie, c'est tout. Qu'il l'attrappe par copie
implique une deuxième. Rien devant le coût d'une exception en
général.

3) Je ne vois pas bien dans votre code où se fait la
destruction de l'exception elle-même. Il faut bien pourtant
qu'elle ait lieu.


C'est le problème du compilateur, non le sien. Le programmeur
n'a pas le droit de detruire l'exception lui-même.

4) Pour ce qui est de sa création correcte, ça n'apparaît pas
dans le code soumis... Comment écrivez-vous les constructeurs,
et l'instruction throw ?


Il y a beaucoup qui n'apparaît pas dans ce qu'il a posté. Tout
ce qu'il a posté semble correct, mais on ne peut pas exclure une
erreur dans les autres parties.

5) Quand vous suivez votre code pas à pas au debugger (en
entrant bien partout), où exactement dans le code obtenez-vous
l'erreur ?


D'après ce qu'il a dit, il obtient l'erreur bien après ce
qu'elle a eu lieu. C'est le propre des erreurs de ce genre. Et
un déboggueur y est à peu près inutile.

Ce qu'il lui faut, évidemment, c'est quelque chose comme Purify.
Mais ce n'est pas donné, s'il est étudiant ou programmeur
amateur. (S'il est programmeur professionnel, évidemment, sa
boîte ferait bien de l'acheter, malgré le prix. Parce qu'elle
regagnera largement le prix en temps de programmeur gagné.)

--
James Kanze GABI Software
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
Patrick 'Zener' Brunet
Bonjour.

Patrick 'Zener' Brunet wrote:

Je réponds à

[...] le code


AMHA il y a quelques gros problèmes de conception là-dedans :

1) Vous utilisez une exception qui contient elle-même des
composants qui font de l'allocation dynamique, et donc qui
sont susceptible de déclencher avec un e des exceptions : vous
jouez avec le feu. Il serait plus sage, autant que possible,
de disposer d'une exception (voire d'un petit pool) qui soit
(entièrement) allouées à l'avance, de manière à ne pas
introduire d'éventualité d'échec.


Oui et non. Tout dépend de l'application -- souvent, on peut
raisonablement supposer qu'on n'épuisera pas la mémoire, et
aborter si on le fait (d'autant plus que souvent, on n'a pas le
choix -- le système d'exploitation le fait pour nous). Alors, on
positionne le new_handler, et on s'occupe d'autres problèmes.

Sinon, il faut certainement des classes faites sur mésure dans
l'exception, pour gerer des chaînes sans allocation dynamique.



Il m'est arrivé de développer de telles classes : elles sont construites
comme des objets globaux, sans allocation de mémoire dynamique, et donc
elles ne sont plus jamais construites ni détruites : seulement
réquisitionnées puis restituées (elles fournissent une méthode static
Throw() ).
Pour ce qui est de la chaîne message, en fait j'avais défini un protocole
permettant de stocker dans le corps de l'exception les éléments variants (2
entiers et un tableau de 127 + 1 caractères), la classe Exception offrant
une fonction type sprintf() contrôlée pour utiliser ça.

Ca permet donc de justifier ma réponse à la suite...

Sinon pour ce qui est de l'abort(), étant plutôt orienté
informatique-industrielle-sans-renoncement-à-la-mémoire-dynamique,
évidemment c'est hors de question (et donc il faudra que je me remette un
peu au courant des évolutions de la gestion "optimiste" de mémoire sous
Linux depuis 2 ans).

2) Votre exception est transmise par copie semble-t-il, et non
par adresse.


Toute exception est transmise par copie. Il le faut bien,
puisqu'on va déballer la pile, et la mémoire où l'utilisateur le
construit (comme temporaire) va disparaître.



Avec ma convention précédente, justement, c'était un pointeur vers
l'exception qui était transmis, lui donc par copie en effet.

Cela implique tout un tas de cycles de
création/copie/destruction de chaînes, ce qui est d'une part
très inefficace, et d'autre part remultiplie les cas d'échec.


Ça implique *une* copie, c'est tout. Qu'il l'attrappe par copie
implique une deuxième. Rien devant le coût d'une exception en
général.



Et s'il utilise throw; dans un bloc catch() pour la relancer ?
Je me souviens-là des tests que j'ai pu faire lors de la mise au point d'une
classe genre "string", au niveau de l'opérateur d'affectation et du
constructeur de copie (et en prenant en compte l'instance anonyme
transitoire lorsqu'on retourne un objet entier plutôt qu'un pointeur). Donc,
selon comment cette instance d'exception est construite, remplie puis
"lancée", j'hésite sur le nombre possible de copies.

3) Je ne vois pas bien dans votre code où se fait la
destruction de l'exception elle-même. Il faut bien pourtant
qu'elle ait lieu.


C'est le problème du compilateur, non le sien. Le programmeur
n'a pas le droit de detruire l'exception lui-même.



Donc toujours avec la même convention, le compilo fait ce qu'il veut du
pointeur qui est transmis, mais si un bloc catch() ne relance pas
l'exception, elle doit s'arrêter là.
Dans mes classes, il y avait donc à ce niveau un pE->Release(), comme ça se
fait dans les classes d'exceptions des MFC par exemple (quoiqu'on puisse en
dire). Dès lors que l'exception n'est pas atomique et nécessite une
destruction (ou restitution dans mon cas) "évoluée", finalement je préfère.

4) Pour ce qui est de sa création correcte, ça n'apparaît pas
dans le code soumis... Comment écrivez-vous les constructeurs,
et l'instruction throw ?


Il y a beaucoup qui n'apparaît pas dans ce qu'il a posté. Tout
ce qu'il a posté semble correct, mais on ne peut pas exclure une
erreur dans les autres parties.



Oui, je voudrais voir le throw...

5) Quand vous suivez votre code pas à pas au debugger (en
entrant bien partout), où exactement dans le code obtenez-vous
l'erreur ?


D'après ce qu'il a dit, il obtient l'erreur bien après ce
qu'elle a eu lieu. C'est le propre des erreurs de ce genre. Et
un déboggueur y est à peu près inutile.



Je n'ai pas pu déterminer ce qui était en train d'être détruit, ni à quel
moment. Ca manque...

Ce qu'il lui faut, évidemment, c'est quelque chose comme Purify.
Mais ce n'est pas donné, s'il est étudiant ou programmeur
amateur. (S'il est programmeur professionnel, évidemment, sa
boîte ferait bien de l'acheter, malgré le prix. Parce qu'elle
regagnera largement le prix en temps de programmeur gagné.)


Bienheureux les concepteurs qui ont réussi à recruter un employeur sérieux !

Moi je suis à nouveau en train de chercher, et j'espère que je ne vais pas à
nouveau devoir rafistoler les moutons crevés laissés par le type d'avant, la
conception des produits d'avenir de la boîte étant dans le même temps
confiée à des bricolos pour raisons de budget :-@

Heureusement qu'on peut encore faire de la R&D sérieuse durant ses loisirs
:-D

Cordialement,

--
/***************************************
* Patrick BRUNET
* E-mail: lien sur http://zener131.free.fr/ContactMe
***************************************/



Avatar
kanze
Patrick 'Zener' Brunet wrote:

Patrick 'Zener' Brunet wrote:

Je réponds à

[...] le code


AMHA il y a quelques gros problèmes de conception là-dedans :

1) Vous utilisez une exception qui contient elle-même des
composants qui font de l'allocation dynamique, et donc qui
sont susceptible de déclencher avec un e des exceptions :
vous jouez avec le feu. Il serait plus sage, autant que
possible, de disposer d'une exception (voire d'un petit
pool) qui soit (entièrement) allouées à l'avance, de
manière à ne pas introduire d'éventualité d'échec.


Oui et non. Tout dépend de l'application -- souvent, on peut
raisonablement supposer qu'on n'épuisera pas la mémoire, et
aborter si on le fait (d'autant plus que souvent, on n'a pas
le choix -- le système d'exploitation le fait pour nous).
Alors, on positionne le new_handler, et on s'occupe d'autres
problèmes.

Sinon, il faut certainement des classes faites sur mésure
dans l'exception, pour gerer des chaînes sans allocation
dynamique.


Il m'est arrivé de développer de telles classes : elles sont
construites comme des objets globaux, sans allocation de
mémoire dynamique, et donc elles ne sont plus jamais
construites ni détruites : seulement réquisitionnées puis
restituées (elles fournissent une méthode static Throw() ).


Je ne comprends pas trop cette association pas d'allocation
dynamique -- objet global. J'ai bien des objets globaux qui font
de l'allocation dynamique. (J'ai même parfois des std::string
globaux.)

Pour ce qui est de la chaîne message, en fait j'avais défini
un protocole permettant de stocker dans le corps de
l'exception les éléments variants (2 entiers et un tableau de
127 + 1 caractères), la classe Exception offrant une fonction
type sprintf() contrôlée pour utiliser ça.


Tu n'as pas besoin d'un protocol, parce que tu pourrais réserver
aussi un tableau de caractères dans n'importe quelle classe
dérivée. (Tableau de type, s'entend, pourqu'il n'y a pas
d'allocation dynamique.) Certaines implémentations des
std::exception le font, je crois ; si tu passes un chaîne plus
long que le tableau fixe, elles essaient d'obtenir de la mémoire
dynamique (avec new (nothrow)), et si ça échoue, elles tronquent
la chaîne.

Mais ça n'évite pas totallement le besoin des allocations
dynamique. Le problème, c'est qu'il faut que le système stocke
l'exception même quelque part. Et que ça ne peut pas être sur la
pile, parce qu'elle fait detruire la pile. Et que ça ne peut pas
être en mémoire statique, parce qu'il peut y avoir d'autres
exceptions lors du stack unwinding. (A propos : comment est-ce
qu'on dit « stack unwinding » en français ?) Alors,
l'implémentation est obligée à allouer la mémoire pour
l'exception dynamiquement. (En général, l'implémentation utilise
un tas ou une pile à part pour ça. Et certainement pas operator
new. Mais c'est dynamique quand même.)

Ca permet donc de justifier ma réponse à la suite...

Sinon pour ce qui est de l'abort(), étant plutôt orienté
informatique-industrielle-sans-renoncement-à-la-mémoire-dynamique,
évidemment c'est hors de question (et donc il faudra que je me
remette un peu au courant des évolutions de la gestion
"optimiste" de mémoire sous Linux depuis 2 ans).


Je suis sceptique en ce qui concerne les possibilités d'un
logiciel critique avec utilisation de la mémoire dynamique.

Mais il y a bien des programmes industriels qui ne sont pas
critiques, et qui peuvent supporter un abort, pourvu que la
probabilité ou la fréquence en soit assez faible. (À vrai dire,
tout système critique doit être secouru, aussi. De façon à ce
qu'un crash ne soit pas fatal pour le système entier.) De toute
façon, si tu te bases sur un système d'exploitation courante,
Windows, ou un Unix, il faut l'accepter, parce qu'ils abortent
tous le processus si l'épuisement de la mémoire se produit lors
de l'agrandissement de la pile.

Aussi, on accepte souvent à prendre la « risque », en s'assurant
qu'il n'y a que l'application en question qui tourne sur la
machine, et que la machine a assez de mémoire pour ne pas en
épuiser.

2) Votre exception est transmise par copie semble-t-il, et
non par adresse.


Toute exception est transmise par copie. Il le faut bien,
puisqu'on va déballer la pile, et la mémoire où
l'utilisateur le construit (comme temporaire) va
disparaître.


Avec ma convention précédente, justement, c'était un pointeur
vers l'exception qui était transmis, lui donc par copie en
effet.


D'accord. Tu as donc un objet statique, et tu lèves une
exception du type pointeur à l'objet. Seulement, qu'est-ce qui
se passe si les exceptions s'imbriquent, et que tu veux lever
une deuxième exception du même type, alors que la première est
toujours active ? Ou est-ce que tu as conçu l'application pour
que le cas ne peut pas se produire ?

Cela implique tout un tas de cycles de
création/copie/destruction de chaînes, ce qui est d'une
part très inefficace, et d'autre part remultiplie les cas
d'échec.


Ça implique *une* copie, c'est tout. Qu'il l'attrappe par
copie implique une deuxième. Rien devant le coût d'une
exception en général.


Et s'il utilise throw; dans un bloc catch() pour la relancer ?


Pas de copie, je crois. Je ne suis pas sûr, mais je crois que le
throw sans paramètres est garantie de réutiliser l'objet
existant.

Je me souviens-là des tests que j'ai pu faire lors de la mise
au point d'une classe genre "string", au niveau de l'opérateur
d'affectation et du constructeur de copie (et en prenant en
compte l'instance anonyme transitoire lorsqu'on retourne un
objet entier plutôt qu'un pointeur). Donc, selon comment cette
instance d'exception est construite, remplie puis "lancée",
j'hésite sur le nombre possible de copies.


Le nombre de copies n'est de toute façon pas bien spécifié.
Conceptuellement, tu construis l'objet « sur la pile » avec une
expression du genre X(), et ensuite, cet objet est copié
ailleurs. Mais un compilateur a le droit de faire en sort que
l'objet temporaire que tu construis, et la copie « ailleurs »,
sont en fait le même objet, et supprimer l'action de copier.

Si tu attrappes l'exception par référence, en revanche,
l'implémentation n'a pas droit à une autre copie. Et si je ne me
trompe pas, c'est aussi vrai si tu relèves la même exception
avec un throw sans paramètres dans un bloc de catch.

3) Je ne vois pas bien dans votre code où se fait la
destruction de l'exception elle-même. Il faut bien pourtant
qu'elle ait lieu.


C'est le problème du compilateur, non le sien. Le
programmeur n'a pas le droit de detruire l'exception
lui-même.


Donc toujours avec la même convention, le compilo fait ce
qu'il veut du pointeur qui est transmis, mais si un bloc
catch() ne relance pas l'exception, elle doit s'arrêter là.


Tout à fait. En fait, il est garantie que le destructeur de
l'objet levé soit appelé. Et pas avant la fin du dernier bloc de
catch qui le concerne. Je crois même que le moment exact de la
destruction est fixée, mais je ne suis pas 100% sûr.

En revanche, si tu lèves des pointeurs, c'est un pointeur qui
est detruit, et la destruction d'un pointeur est en fait un
no-op.

Dans mes classes, il y avait donc à ce niveau un
pE->Release(), comme ça se fait dans les classes d'exceptions
des MFC par exemple (quoiqu'on puisse en dire). Dès lors que
l'exception n'est pas atomique et nécessite une destruction
(ou restitution dans mon cas) "évoluée", finalement je
préfère.

4) Pour ce qui est de sa création correcte, ça n'apparaît
pas dans le code soumis... Comment écrivez-vous les
constructeurs, et l'instruction throw ?


Il y a beaucoup qui n'apparaît pas dans ce qu'il a posté.
Tout ce qu'il a posté semble correct, mais on ne peut pas
exclure une erreur dans les autres parties.


Oui, je voudrais voir le throw...

5) Quand vous suivez votre code pas à pas au debugger (en
entrant bien partout), où exactement dans le code
obtenez-vous l'erreur ?


D'après ce qu'il a dit, il obtient l'erreur bien après ce
qu'elle a eu lieu. C'est le propre des erreurs de ce genre.
Et un déboggueur y est à peu près inutile.


Je n'ai pas pu déterminer ce qui était en train d'être
détruit, ni à quel moment. Ca manque...


Un peu d'instrumentation du code, ça ne fait jamais mal.

Ce qu'il lui faut, évidemment, c'est quelque chose comme
Purify. Mais ce n'est pas donné, s'il est étudiant ou
programmeur amateur. (S'il est programmeur professionnel,
évidemment, sa boîte ferait bien de l'acheter, malgré le
prix. Parce qu'elle regagnera largement le prix en temps de
programmeur gagné.)


Bienheureux les concepteurs qui ont réussi à recruter un
employeur sérieux !


En effet. J'ai vu le problème plus d'une fois. On a une équipe
de programmeurs. Qui coûte, avec les charges et al., environ
500 000 Euros. Et on réfuse l'achat d'un outil à 10 000 Euros
qui leur ferait gagner 20% de temps -- c-à-d 100 000 Euros par
an.

Mais d'après ce que je vois, la situation s'améliore, et que
l'utilisation de Purify est quasi-universelle chez mes clients
aujourd'hui.

Moi je suis à nouveau en train de chercher, et j'espère que je
ne vais pas à nouveau devoir rafistoler les moutons crevés
laissés par le type d'avant, la conception des produits
d'avenir de la boîte étant dans le même temps confiée à des
bricolos pour raisons de budget :-@

Heureusement qu'on peut encore faire de la R&D sérieuse durant
ses loisirs :-D


Figure-toi qu'il y a, aujourd'hui, des boîtes qui savent estîmer
un travail bien fait, et qui sont prêt à donner aux développeurs
ce qu'il faut pour le faire. (Évidemment, elles n'embauchent pas
tous les jours. Mais en cherchant assez longtemps, on finit par
les dénicher.)

--
James Kanze GABI Software
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
superbabar51
Bonsoir,

Je ne vous ferai pas perdre plus votre temps.
En effet, l'exception est maintenant déclanchée par le constructeur
et non plus par le destructeur alors que je n'ai pas changé le code
(moi rien comprendre). Le pb problème doit donc être plus général.
Va falloir revoir l'"architecture" du programme. (au fait, étudiant et
pas programmeur... Mais deux ans sans codé et on oublie vite la
théorie et les bases: analyser avant programmer)

Pour ceux que ça interesse.. (j'aimerai bien comprendre comme même en
fait ;-))

CBSP::CBSP(string sFileName)
{
/* ... */
if(sFileName[0] != '' && sFileName[1] != ':')
{
// then sFileName probably does NOT contain a full path to the file
m_sFileName = Config::GameDir;
m_sFileName = m_sFileName + "Maps";

/************ Exception déclanchée par cette ligne */
m_sFileName = m_sFileName + sFileName;
} else
m_sFileName = sFileName;
}

Voici le "Call Stack":
CrazyMarket.exe!strlen() Line 78 Asm
CrazyMarket.exe!exception::exception(const exception & that={...}) +
0x31 C++
CrazyMarket.exe!std::bad_alloc::bad_alloc(const std::bad_alloc &
__that={...}) + 0x13 C++
CrazyMarket.exe!std::_Nomemory() Line 9 + 0xd C++
CrazyMarket.exe!operator new(unsigned int size!) Line 15 C++
CrazyMarket.exe!std::_Allocate<char>(unsigned int _Count!, char *
__formal=0x00000000) Line 34 + 0x9 C++

CrazyMarket.exe!std::allocator<char>::allocate(unsigned int
_Count!) Line 138 C++

CrazyMarket.exe!std::basic_string<char,std::char_traits<char>,std::allocat or<char>
::_Copy(unsigned int _Newsize , unsigned int _Oldlen=0) Line 1458 +
0xf C++


CrazyMarket.exe!std::basic_string<char,std::char_traits<char>,std::allocat or<char>
::_Grow(unsigned int _Newsize , bool _Trimúlse) Line 1485 C++


CrazyMarket.exe!std::basic_string<char,std::char_traits<char>,std::allocat or<char>
::assign(const
std::basic_string<char,std::char_traits<char>,std::allocator<char> > &

_Right={...}, unsigned int _Roff=0, unsigned int _CountB94967295)
Line 599 + 0x10 C++

CrazyMarket.exe!std::basic_string<char,std::char_traits<char>,std::allocat or<char>
::assign(const
std::basic_string<char,std::char_traits<char>,std::allocator<char> > &

_Right={...}) Line 586 C++

CrazyMarket.exe!std::basic_string<char,std::char_traits<char>,std::allocat or<char>
::operator=(const
std::basic_string<char,std::char_traits<char>,std::allocator<char> > &

_Right={...}) Line 468 C++

CrazyMarket.exe!CBSP::CBSP(std::basic_string<char,std::char_traits<char>,s td::allocator<char>
sFileName={...}) Line 58 C++
CrazyMarket.exe!WinMain(HINSTANCE__ * hInstance=0x00400000,

HINSTANCE__ * hPrevInstance=0x00000000, char * lpCmdLine=0x00141f07,
int nCmdShow=1) Line 29 + 0x4f C++
CrazyMarket.exe!WinMainCRTStartup() Line 251 + 0x30 C
kernel32.dll!7c816d4f()
ntdll.dll!7c925b4f()
kernel32.dll!7c8399f3()

exception nomemory? alors que le stack contient seulement d'une string
(et en plus, les chaines en elle même sont en mémoire dynamique,
corrigez si je me trompe)

Dans tous les cas:
Merci à tous pour vos postes!

Avatar
kanze
wrote:

En effet, l'exception est maintenant déclanchée par le
constructeur et non plus par le destructeur alors que je n'ai
pas changé le code (moi rien comprendre). Le pb problème doit
donc être plus général. Va falloir revoir l'"architecture" du
programme. (au fait, étudiant et pas programmeur... Mais deux
ans sans codé et on oublie vite la théorie et les bases:
analyser avant programmer)

Pour ceux que ça interesse.. (j'aimerai bien comprendre comme
même en fait ;-))

CBSP::CBSP(string sFileName)
{
/* ... */
if(sFileName[0] != '' && sFileName[1] != ':')
{
// then sFileName probably does NOT contain a full path to the file
m_sFileName = Config::GameDir;
m_sFileName = m_sFileName + "Maps";

/************ Exception déclanchée par cette ligne */
m_sFileName = m_sFileName + sFileName;
} else
m_sFileName = sFileName;
}


Qui ne comporte pas d'erreur visible. (Je suppose que
m_sFileName est une variable membre de type std::string.)

Voici le "Call Stack":
CrazyMarket.exe!strlen() Line 78 Asm
CrazyMarket.exe!exception::exception(const exception & that={...})
+

0x31 C++
CrazyMarket.exe!std::bad_alloc::bad_alloc(const std::bad_alloc &
__that={...}) + 0x13 C++


Il manque de mémoire.

CrazyMarket.exe!std::_Nomemory() Line 9 + 0xd C++
CrazyMarket.exe!operator new(unsigned int size!) Line 15 C++
CrazyMarket.exe!std::_Allocate<char>(unsigned int _Count!, char *
__formal=0x00000000) Line 34 + 0x9 C++

CrazyMarket.exe!std::allocator<char>::allocate(unsigned int
_Count!) Line 138 C++


CrazyMarket.exe!std::basic_string<char,std::char_traits<char>,std::allocat or<char>

::_Copy(unsigned int _Newsize , unsigned int _Oldlen=0) Line 1458
+


0xf C++


CrazyMarket.exe!std::basic_string<char,std::char_traits<char>,std::allocat or<char>

::_Grow(unsigned int _Newsize , bool _Trimúlse) Line 1485 C++



CrazyMarket.exe!std::basic_string<char,std::char_traits<char>,std::allocat or<char>

::assign(const
std::basic_string<char,std::char_traits<char>,std::allocator<char> >

&

_Right={...}, unsigned int _Roff=0, unsigned int _CountB94967295)
Line 599 + 0x10 C++


CrazyMarket.exe!std::basic_string<char,std::char_traits<char>,std::allocat or<char>

::assign(const
std::basic_string<char,std::char_traits<char>,std::allocator<char> >

&

_Right={...}) Line 586 C++


CrazyMarket.exe!std::basic_string<char,std::char_traits<char>,std::allocat or<char>

::operator=(const
std::basic_string<char,std::char_traits<char>,std::allocator<char> >

&

_Right={...}) Line 468 C++


CrazyMarket.exe!CBSP::CBSP(std::basic_string<char,std::char_traits<char>,s td::allocator<char>

sFileName={...}) Line 58 C++
CrazyMarket.exe!WinMain(HINSTANCE__ * hInstance=0x00400000,

HINSTANCE__ * hPrevInstance=0x00000000, char * lpCmdLine=0x00141f07,
int nCmdShow=1) Line 29 + 0x4f C++
CrazyMarket.exe!WinMainCRTStartup() Line 251 + 0x30 C
kernel32.dll!7c816d4f()
ntdll.dll!7c925b4f()
kernel32.dll!7c8399f3()

exception nomemory? alors que le stack contient seulement
d'une string (et en plus, les chaines en elle même sont en
mémoire dynamique, corrigez si je me trompe)


C'est effectivement une exception due à une manque de mémoire,
detecter lors d'une allocation pour créer une chaîne. Il peut y
avoir deux explications : ou bien, tu as quelque chose ailleurs
qui a bouffé toute la mémoire, ou bien, tu as fait quelque chose
avant qui a corrompu l'espace où la mémoire libre se trouve, de
façon à ce que l'allocateur n'en trouve plus.

Dans les deux cas, la cause réele du problème se trouve en
amont.

--
James Kanze GABI Software
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


1 2