OVH Cloud OVH Cloud

new et bad_alloc

28 réponses
Avatar
pasde.bcausse.spam
bonsoir, je reviens avec mon probleme.

class RX_A {

public :
unsigned long l;
unsigned char m;
unsigned char d;
signed char bas;
signed char haut;
};

class RX_B{

public :
RX_A premier;
RX_A second;
};


class RX_C {

RX_B* table;

public:

RXHashTable();
~RXHashTable();

};

dans RX_C.cpp

RX_C::RX_C() : table(0) {

const unsigned long capacity = 1<<31; //2147483648

try {
table = new RX_B[capacity];

if(table == 0)
std::cerr << "table = 0 -> RX_C::RX_C: cannot
allocate the table\n";

} catch (const std::bad_alloc &e_ba) {
std::cerr << "RX_C::RX_C: cannot allocate the
table\n";
throw;
}


}

non seulement table != 0 mais aucune exception bad_alloc est levée.

comprends pas :-(


--
Bruno Causse
http://perso.wanadoo.fr/othello

8 réponses

1 2 3
Avatar
Fabien LE LEZ
On Thu, 26 May 2005 21:34:43 +0200, James Kanze :

Alors, on se sert de celui qu'on veut à
l'initialisation, et on ne met pas de commentaire pour faire
penser que peut-être on voulait l'autre.


D'un autre côté, quand on écrit

unsigned long machin= 2147483648;

il vaut mieux indiquer quelque part qu'il s'agit de 1<<31 : on ne le
reconnaît pas forcément comme tel au premier coup d'oeil.
(Même si je reconnais tout de suite 65536, au-delà j'ai du mal...)

Avatar
James Kanze
Bruno CAUSSE wrote:
dans l'article ,
à a écrit le 26/05/05 10:05 :


De toute façon, si son problème, c'est simplement de tester le
comportement de son code en cas d'épuisement de la mémoire,



Je debute, alors je suis une regle (recommandation) de
verifier que l'allocation n'echoue pas. Et j'essaye de le
faire avec la "philosophie" C++ (bad_alloc) plutot que = > NULL.


Mais cela me servira a liberer les ressources (fichers,
sockets ...) avant de quitter le prog.


Voir mes réponses par ailleurs. Et faire une recherche sous RAII
sur la toile. En gros, Si tu attrapes l'exception (ou plutôt
toutes les exceptions) au plus haut niveau, tu es assuré que
tous les déstructeurs des variables locales sont appelés. Et si
ce sont les destructeurs qui sont responsable de la libération
des ressources, il ne doit plus y avoir de problème.

--
James Kanze mailto:
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 pl. Pierre Sémard, 78210 St.-Cyr-l'École, France +33 (0)1 30 23 00 34


Avatar
James Kanze
Fabien LE LEZ wrote:
On Thu, 26 May 2005 10:24:34 +0200, Bruno CAUSSE :


Mais cela me servira a liberer les ressources (fichers,
sockets ...) avant de quitter le prog.



Note qu'un OS décent est censé être capable de fermer les
ressources lors de la fermeture d'un process (y compris la
fermeture brutale, par kill sous Unix ou le gestionnaire des
tâches sous Windows), voire même de supprimer les fichiers
temporaires.


« Dream on », comme on dit aux USA. Au plus, le système peut
libérer les ressources qu'il connaît -- en général, il ne libère
que les ressources dont il est 100% sûr qu'elles ne peuvent
servir qu'au processus. Donc, par exemple, Unix (et Windows est
sans doute pareil) libère la mémoire allouée au processus, il
ferme les fichiers ouverts dans le processus, il libère des
locks sur des fichiers detenus par le processus, et les mutex,
sémaphores, etc., detenus par le processus. En revanche, il
n'ose pas effacer les fichiers temporaires (dont d'autres
processus peuvent servir), ni les processus déscendants, ni un
tas d'autres ressources qui peuvent, a priori, être partagées
entre plusieurs processus. Et a plus fortiori, il ne libère pas
les ressources qu'il connaît pas, comme des tables temporaire
que le processus a créé dans la base de données, le chariot
d'achat sur la site distante, etc.

(En passant, en ce qui concerne les fichiers temporaires, mon
gestionnaire en a une option -- une fonction statique -- pour
dire de ne pas les effacer. C'est drôlement commode en phase de
mise au point. Sinon, sous Unix encore, si on les crée dans un
répertoire particulier, ils disparaîtron au prochain reboot.
C-à-d dans quelques années sur les systèmes sur lesquels je
travaille.)

En pratique, il y a même des chances pour que le processus
soit tué par l'utilisateur plus souvent qu'il ne rencontrera
un bad_alloc.


Et typiquement, quand un utilisateur tue un processus de façon
brutale (kill -9, sous Unix), il y a une fuite de ressources.

--
James Kanze mailto:
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 pl. Pierre Sémard, 78210 St.-Cyr-l'École, France +33 (0)1 30 23 00 34


Avatar
Fabien LE LEZ
On Thu, 26 May 2005 22:00:15 +0200, James Kanze :

En revanche, il
n'ose pas effacer les fichiers temporaires


Sauf si (sous Windows au moins) il a été indiqué, à la création du
fichier, qu'il doit être effacé à la fermeture.

ni les processus déscendants


Ça, par contre, je ne connais pas -- ça n'existe pas sous Windows.

En fait, on travaille vraiment dans deux domaines différents, et donc
deux optiques très différentes : je fais des applications GUI qui ont
rarement l'occasion d'être lancées pendant plus de quelques heures de
rang, sur des machines redémarrées au moins une fois par mois (voire
même, une fois par jour, pour ceux qui éteignent leur PC).

Avatar
Bruno CAUSSE
dans l'article 429624cf$0$10958$, James Kanze à
a écrit le 26/05/05 21:34 :

Bruno Causse wrote:

const unsigned long capacity = 1<<31; //2147483648


Une petite question sécondaire : capacity, est-ce qu'il doit
avoir la valeur 2147483648, ou est que tu veux une valeur avec
le bit 31 à 1, et les autres à zéro ?


Seulement une valeur pour avoir un bad_alloc :-)


Je sais que c'est la même chose à la fin, mais conceptuellement,
il y a une différence.


Une explication, merci

Alors, on se sert de celui qu'on veut à
l'initialisation, et on ne met pas de commentaire pour faire
penser que peut-être on voulait l'autre.


?


Avatar
kanze
Fabien LE LEZ wrote:
On Thu, 26 May 2005 22:00:15 +0200, James Kanze :

En revanche, il
n'ose pas effacer les fichiers temporaires


Sauf si (sous Windows au moins) il a été indiqué, à la
création du fichier, qu'il doit être effacé à la fermeture.


C'est effectivement une option qui manque sous Unix.
Traditionnellement, on contourne le problème en faisant un
remove dès que l'ouverture à réussi, mais c'est une méthode un
peu lourde et pas très transparente. Et qui ne marche pas si
d'autres processus doivent pouvoir trouver le fichier tant qu'on
tourne.

Ceci dit, le problème reste entier. D'un certain point de vue,
tout fichier de sortie est temporaire, jusqu'à ce que tu as fini
de l'écrire. Si le programme s'arrête d'une façon intempestive,
il faut bien l'effacer, pour que personne n'essaie de se servir
du fichier partiel. Donc, par exemple, les ofstream sont
encapsuler dans un wrapper dont le destructeur est à peu près :

if ( myFile.is_open() ) {
myFile.close() ;
remove( myFilename.c_str() ) ;
}

Si on arrive au destructeur sans avoir fermer le fichier, c'est
qu'il y a eu une erreur, et le fichier n'est pas bon.

Bien que, sous Unix, il y a une grande tradition de faire
confiance à l'utilisateur, et on considère souvent qu'un message
et un code de retour non nul suffit (d'autant plus que le
fichier en sortie, c'est souvent std::cout, dont on ne connaît
pas le nom). Dans les scripts, donc, on écrit normalement des
choses comme :

myprog params > sortie || rm sortie

(Les make du Unix supprime normalement le cible si un programme
qu'il invoque renvoie non nul. Ce qui a le même effet.)

En interactif, on compte sur l'utilisateur d'en faire autant.

AMHA, ce n'est pas forcement une bonne tradition.

ni les processus déscendants


Ça, par contre, je ne connais pas -- ça n'existe pas sous Windows.


Bien sûr que si. Qu'est-ce que fait CreateProcess, sinon ?

Le problème, c'est que certains processus n'ont de sens que si
le processus parent est actif ; d'autre, en revanche, fonctionne
de façon autonome -- ce n'est même pas rare d'avoir des
processus dont le seul but est d'en lancer d'autres, puis
disparaître.

Là aussi, sous Unix, au moins, il existe des moyens qui peuvent
servir dans beaucoup de cas -- souvent, par exemple, la
communication entre les deux processus se fait par une pipe, et
si un processus essaie d'écrire dans une pipe où personne
n'écoute plus, il est tué (et s'il essaie de lire où personne
n'écrit plus, il a fin de fichier, et peut donc s'arrêter tout
seul). Mais c'est limité.

Mais enfin, mon point est clair, je crois. Il existe des
ressources dont le système ne peut pas savoir s'il faut les
libérer ou non.

En fait, on travaille vraiment dans deux domaines différents,
et donc deux optiques très différentes : je fais des
applications GUI qui ont rarement l'occasion d'être lancées
pendant plus de quelques heures de rang, sur des machines
redémarrées au moins une fois par mois (voire même, une fois
par jour, pour ceux qui éteignent leur PC).


Mais je ne crois pas que ça change quelque chose du principe ;
seulement, peut-être, de la fréquence du problème. Par exemple,
mon dernier exemple reste valide : tu crées des tables
temporaires dans une base de données sur une autre machine. Qui
les supprime ?

En fait, la plupart des bases de données ont un domaine spécial
pour ça ; le même nom donne accès à un domaine distinct pour
chaque connection, et le domaine complet est effacé à la fin de
la connection. Mais il peut arriver que tu veux garder des
tables au delà d'une seule connection. Il y a aussi la
possibilité au démarrage de aller voir, et d'effacer tout ce qui
ne doit pas y être. Et sans doute d'autres possibilités.

Tout ce que je voulais dire, c'est que compter sur le système
d'automatiquement s'occuper de tout, c'est un peu naïf. Tu dois
reflechir toi-même à tous ces cas. Si ta reflection t'amène à
conclure que le système s'en occupe d'une façon adéquate, tant
mieux. C'est moins de boulot de programmation pour toi. Mais tu
dois considérer la question quand même.

--
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
kanze
Fabien LE LEZ wrote:
On Thu, 26 May 2005 21:34:43 +0200, James Kanze :

Alors, on se sert de celui qu'on veut à
l'initialisation, et on ne met pas de commentaire pour faire
penser que peut-être on voulait l'autre.


D'un autre côté, quand on écrit

unsigned long machin= 2147483648;

il vaut mieux indiquer quelque part qu'il s'agit de 1<<31 : on
ne le reconnaît pas forcément comme tel au premier coup
d'oeil.


Mais justement. Ce que j'ai dit, c'est que s'il veut 1 << 31, il
doit écrire 1 << 31, et non 2147483648. Il ne doit écrire
2147483648 que si ce qu'il veut est bien 2147483648, et que le
fait que ça soit 1 << 31 soit pûrement coïncidental.

J'avoue que la probabilité qu'on veut précisement cette valeur
comme constante, et que la raison ne soit pas lié au fait
qu'elle soit 2^31, me semble assez faible. Mais admettons. Mon
point, c'est que la valeur a une signification, et que dans la
mesure du possible, on doit l'écrire de façon que la
signification soit apparente sans commentaire. Donc, si je veux
2^31, je veux 2^31, independamment de ce qu'il vaut 2147483648
ou d'autre chose. J'écris donc 1<<31, et non 2147483648, et un
commentaire pour dire que ça vaut 2147483648 est superflu, parce
que cette valeur est sans signification. (À la rigueur,
j'admettrais un commentaire pour dire qu'elle vaut 2^31, si ce
que je veux, c'est 2^31, en tant que 2^31, et non une valeur
avec seulement le bit 31 à 1.)

(Même si je reconnais tout de suite 65536, au-delà j'ai du
mal...)


En fait, au delà, j'écrirai plutôt quelque chose du genre
2*1024*1024. Si je voulais la valeur 2 Méga, avec la
signification informatique de Méga. Pas si je voulais une valeur
avec seulement le bit 31 à 1.

Je ne sais pas si je m'exprime d'une façon clairement. Il est
évident que 1<<31, 2^31, 2147483648, et 2*1024*1024 ont la même
valeur, et qu'en ce qui concerne le compilateur, on peut les
substituer au gré sans rien changer -- en supposant une machine
32 bits, au moins. Mais AMHA, la forme de l'écriture qu'on
choisit doit refléter la raison pourquoi on veut cette valeur.
Et qu'une fois on en a donné la raison, la valeur numérique en
tant que tel n'a pas réelement de l'importance.

--
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
kanze
Bruno CAUSSE wrote:
dans l'article 429624cf$0$10958$, James Kanze à
a écrit le 26/05/05 21:34 :

Bruno Causse wrote:

const unsigned long capacity = 1<<31; //2147483648


Une petite question sécondaire : capacity, est-ce qu'il doit
avoir la valeur 2147483648, ou est que tu veux une valeur
avec le bit 31 à 1, et les autres à zéro ?


Seulement une valeur pour avoir un bad_alloc :-)


La plus simple, alors, c'est « throw std::bad_alloc »:-).

On peut aussi vouloir tester le new_handler. Là, c'est plus
difficile, parce qu'il faut réelement déborder l'allocation.
Dans ce cas-là, je crois que j'écrirais :

new char[ std::numeric_limits< size_t >::max() ] ;

Encore que j'ai vu au moins une implémentation où ça ne
suffisait pas ; il fallait le faire dans une boucle. Mais c'est
la plus grande nombre d'octets que tu peux démander.

En fait, je soustrairais une facteur « fudge », pour le cas où
le compilateur ajoute un peu, sans tester pour débordement. Bien
qu'avec les compilateurs que je connais, ils n'ajoutent un peu
que si le type alloué a un destructeur non trivial. Mais a
priori, tu as autant de chances à échouer avec un « -(16 *
sizeof( void* ) ) » en plus que sans, et je doute fort qu'il
existe, ni existera jamais, un compilateur qui ajoute plus que
16*sizeof(void*).

Je sais que c'est la même chose à la fin, mais
conceptuellement, il y a une différence.


Une explication, merci


C'est que tu n'as pas choisi cette valeur au hazard. Il y avait
bien un motif. Si le motif, c'est qu'il fallait une valeur avec
seulement le bit 31 à un, alors, l'écriture 1<<31 a tout dit. De
même si le but était la valeur 2^31. Le commentaire ajoute une
information sans intérêt (et le fait de l'avoir commenté suggère
fort qu'elle a un intérêt, et même un intérêt important). Si en
revanche la valeur provient d'ailleur, et le fait qu'elle soit
réelement 2^31 n'est qu'une coïncidence, il vaut mieux écrire
2147483648. Et ne pas parler de la coïncidence.

Et comme c'est le cas dont ton cas, tu veux simplement une
valeur « assez large », 2000000000 serait plus suggestive.
Encore que j'aurais une tendance à écrire :

unsigned long huge = 2000000000 ;

avec un nom qui suggère sa signification dans le programme.

--
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 3