OVH Cloud OVH Cloud

Savoir si une classe a ete cree par new

28 réponses
Avatar
dug8C
Bonjour,

J'ai besoin de savoir si une classe est cree sur la pile ou sur le
tas.
J'ai definit un booleen qui porte cette indication, et qui est
renseign=E9 au moment de la creation par une focntoin new priv=E9e


// le fichier h
class CBase1
{

public:

CBase1(const std::string& name);
CBase1(const CBase1& autre);
CBase1& operator=3D(const CBase1& autre);
virtual ~CBase1();

void* operator new( unsigned bytes )
{
// std::cout << "operator new( unsigned bytes )" <<
std::endl;
void* result =3D malloc(bytes);
if (result =3D=3D NULL)
throw std::bad_alloc();
else
{
creation_dyn =3D true;
return result;
}
}

void operator delete (void* p )
{
free(p);
}

// -----
std::string Name() const ;
// renvoie la trace de la creation sur la pile ou sur le tas
bool Dyn_Created() const { return cree_dyn; }

//--- operator

virtual bool operator=3D=3D(const CBase1& autre);

// --
static bool creation_dyn;
protected:
// identifiant
std::string m_name;
// trace de la creation sur la pile ou sur le tas
bool cree_dyn;

virtual void Copy( const CBase1& autre);
};

// -- le fichier cpp
CBase1::CBase1(const std::string& name)
: m_name(name)
{

std::cout << "CBase1 CTOR (" << m_name << ", " << this << ")" <<
std::endl;
cree_dyn =3D creation_dyn;
creation_dyn =3D false;
}

CBase1::CBase1(const CBase1& autre)
{
Copy(autre);
}
CBase1& CBase1::operator=3D(const CBase1& autre)
{
Copy(autre);
return *this;
}
CBase1::~CBase1()
{
std::cout << "CBase1 DTOR (" << m_name << ", " << this << ")" <<
std::endl;
}


std::string CBase1::Name() const
{
return m_name;
}

bool CBase1::operator=3D=3D(const CBase1& autre)
{
// test d'=E9galit=E9 sur les noms pas sur la facon dont la ressource a
=E9t=E9 cr=E9=E9e
return ( autre.m_name =3D=3D m_name );
}

void CBase1::Copy( const CBase1& autre)
{
if( &autre !=3D this )
{
// ne pas copier cree_dyn !
m_name =3D autre.m_name;
}
}

Ca marche plutot bien en apparence, et je me sert de cette classe
comme base pour le polymorphisme.

Par contre j'ai une interrogation, y a t il une autre facon de faire?
J'ai utilis=E9 malloc et free, car la fonction globale new appelle une
2eme fois le constructeur ce qui est genant.

8 réponses

1 2 3
Avatar
Jean-Marc Bourguet
"James Kanze" writes:

On Mar 24, 8:18 pm, Jean-Marc Bourguet wrote:
"James Kanze" writes:
Le hardware du PDP-11 mettait une forte penalité si char n'était pas
signé. Du coup, char est signé sous tous les Unix que je connais,


AIX a un char non signé.


Intéressant. Est-ce qu'il y a une différence de performance entre les
deux ? (Sur un gros IBM, je sais que charger un char signé dans un
régistre coute, ou au moins a coûté, nettement plus cher que d'en charger
un non-signé.)


Je n'ai jamais fait de mesures. Je sais qu'AIX a un char non signe parce
qu'une lib que nous utilisions avait le bug classique de stocker le
resultat de fgetc dans un char. La boucle infinie qui en resulte avec un
char non signe est beaucoup plus visible que le fait que le caractere de
code 255 soit confondu avec la fin de fichier.

A+

--
Jean-Marc
FAQ de fclc++: http://www.cmla.ens-cachan.fr/~dosreis/C++/FAQ
C++ FAQ Lite en VF: http://www.ifrance.com/jlecomte/c++/c++-faq-lite/index.html
Site de usenet-fr: http://www.usenet-fr.news.eu.org



Avatar
James Kanze
On Mar 26, 9:30 am, Jean-Marc Bourguet wrote:
"James Kanze" writes:
On Mar 24, 8:18 pm, Jean-Marc Bourguet wrote:
"James Kanze" writes:
Le hardware du PDP-11 mettait une forte penalité si char n'étai t pas
signé. Du coup, char est signé sous tous les Unix que je connai s,


AIX a un char non signé.


Intéressant. Est-ce qu'il y a une différence de performance entre l es
deux ? (Sur un gros IBM, je sais que charger un char signé dans un
régistre coute, ou au moins a coûté, nettement plus cher que d'en charger
un non-signé.)


Je n'ai jamais fait de mesures.


Tu aurais pû aussi connaître l'assembleur. Je sais par exemple
que sur un mainframe IBM, quand tu lis un octet de la mémoire,
il le complète avec des 0 dans les poids forts, tandis que quand
tu lis un démi-mot (16 bits), il fait une extension de la signe.
Du coup, si tu écris quelque chose du genre a += b, et b est un
type de 8 bits, c'est une seule instruction si a et dans un
règistre et b est non-signé, mais trois (avec aussi un règistre
en plus) si b est signé. Or que pour les short, le rapport est
inversé.

Je sais qu'AIX a un char non signe parce
qu'une lib que nous utilisions avait le bug classique de stocker le
resultat de fgetc dans un char. La boucle infinie qui en resulte avec un
char non signe est beaucoup plus visible que le fait que le caractere de
code 255 soit confondu avec la fin de fichier.


En effet. On doit le remarquait assez vite:-). Tandis qu'avec
char signé... au moins de tomber sur l'adresse de quelqu'un qui
habite l'Haÿ... (Autant que je sache : le français est la seule
langue au monde à utiliser le ÿ, et en français, il n'apparaît
que dans les noms de six communes, dont l'Haÿ-les-Roses est la
plus grande. Du coup, les chances de tomber sur un problème si
char est signé sont assez faibles, au moins de choisir exprès
les textes de tests.)

--
James Kanze (GABI Software) email:
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
dug8C

Et tu marques tous les éléments dans le tableau ? Mais ça
devient compliqué, ton histoire ; il faut que tu notes s'il
faut delete ou non, puis s'il faut delete ou delete[]. Et
qu'est-ce qui se passe si tu as un pointeur initialisé avec
quelque chose comme &array[3] ? Il faut alors noter si l'objet
est le premier dans le tableau ou non. Et que se passe-t-il si
l'utilisateur du premier élément décide qu'il a fini (et donc,
qu'il faut faire delete), et qu'un utilisateur d'un des autres
-> éléments n'en a pas fini ?


Je ne sais pas quel problème tu cherches à résoudre, mais j'ai
l'impression que c'est la fuite en avant.


Je crois que je me suis un peu perdu dans mon raisonnement...


void* operator new[]( unsigned bytes )
{
nb_elem = bytes / sizeof(CBase1) ;


Ce qui n'est pas forcement juste. Le nombre réel en serait bien
inférieur à nb_elem, jamais strictement supérieur, mais nb_elem
peut bien être strictement supérieur au nombre d'éléments.


Je me suis apercu en faisant des essais que nb_elem = ( bytes - 4 ) /
sizeof(CBase1)
mais c'est peut-être spécifique à mon OS.


creation_dyn_tb = true;
void* result = ::operator new [] ( bytes );


Et que se passe-t-il si ::operator new[] lève une exception ?
il manque un throw bad_alloc()


[...]

int
main()
{
Toto* array = new Toto[ 5 ] ;
std::cout << "allocated 5 Toto dynamically" << std::endl ;
for ( int i = 0 ; i < 5 ; ++ i ) {
if ( ! array[ i ].isDym ) {
std::cout << "Oops: not dynamic" << std::endl ;
}
}
Toto local ;
if ( local.isDym ) {
std::cout << "Oops: dynamic" << std::endl ;
}
delete array ;
return 0 ;
}

renvoie :

allocated 5 Toto dynamically
Oops: dynamic
Salut
Segmentation fault

Ce n'est pas ce que j'appellerai « marcher ». (À vrai dire, ça
me semblait évident que cet exemple ne pourrait pas marcher.)

D'un autre cote c'est normal vu que tu créés avec new[] et que tu

libères avec delete.
Si local "se voit" Dynamic c'est parce que que le nombre d'elements
est mal calculé.
Mais c'est une erreur de ma part de marquer les objets dans le
tableau, comme créés sur le tas.
Il faut les marquer comme créé sur la pile, vu qu'ils ne seront pas
detruit un par un, mais l'ensemble du tableau à la fois .

dans ton exemple ca devient :
void*
Toto::operator new[](
size_t n )
{
void* result = ::operator new[]( n ) ;
allocCount = 0;
return result ;
}

Merci pour les explications et le temps passé, je vais revoir tout ça
à tête reposée .


En multi-thread c'est aléatoire, ça peut ne pas marcher si un thread
cree un tableau sur le tas
tant dis qu'un autre en crée un sur la pile puisqu'ils utilise la m ême
variable
statique.


En somme, ça ne marche pas.
Exact :)



Si j'ai bien compris, le std::set sert à la construction pour verifier
le valeur de this avec celle du set?
Si on la trouve on peut déduire si l'objet est créé sur le tas ou pas.


Non. Il sert lors du delete. Si on trouve le pointeur dans
l'ensemble, on appelle delete ; sinon, on ne l'appelle pas.


Je suis pas persuadé d'avoir besoin de stocker les adresses des
pointeurs.
Pour savoir si on appelle delete, autant mettre l'info dans l'objet
lors
de sa construction et de disposer de cette info à sa destruction.

Les objets sont détruits par une autre classe, qui va appeller delete
au vu de cette info:
- Si l'objet a été créés avec new , on appelera delete.
- S'il a été crées sans new, pas d'appel à delete.
- S'il fait partie d'un tableau, pas d'appel à delete : il sera
détruit lors de la
destruction du tableau avec le delete[]


D'autre part cette classe ne gère q'un objet à la fois et pas des
tableaux, donc ca simplifie le problème.


Avatar
James Kanze
On Mar 26, 9:09 pm, wrote:
Et tu marques tous les éléments dans le tableau ? Mais ça
devient compliqué, ton histoire ; il faut que tu notes s'il
faut delete ou non, puis s'il faut delete ou delete[]. Et
qu'est-ce qui se passe si tu as un pointeur initialisé avec
quelque chose comme &array[3] ? Il faut alors noter si l'objet
est le premier dans le tableau ou non. Et que se passe-t-il si
l'utilisateur du premier élément décide qu'il a fini (et donc,
qu'il faut faire delete), et qu'un utilisateur d'un des autres


-> éléments n'en a pas fini ?



Je ne sais pas quel problème tu cherches à résoudre, mais j'ai
l'impression que c'est la fuite en avant.


Je crois que je me suis un peu perdu dans mon raisonnement...



void* operator new[]( unsigned bytes )
{
nb_elem = bytes / sizeof(CBase1) ;


Ce qui n'est pas forcement juste. Le nombre réel en serait bien
inférieur à nb_elem, jamais strictement supérieur, mais nb_elem
peut bien être strictement supérieur au nombre d'éléments.


Je me suis apercu en faisant des essais que nb_elem = ( bytes - 4 ) /
sizeof(CBase1)
mais c'est peut-être spécifique à mon OS.



creation_dyn_tb = true;
void* result = ::operator new [] ( bytes );


Et que se passe-t-il si ::operator new[] lève une exception ?


il manque un throw bad_alloc()

[...]



int
main()
{
Toto* array = new Toto[ 5 ] ;
std::cout << "allocated 5 Toto dynamically" << std::endl ;
for ( int i = 0 ; i < 5 ; ++ i ) {
if ( ! array[ i ].isDym ) {
std::cout << "Oops: not dynamic" << std::endl ;
}
}
Toto local ;
if ( local.isDym ) {
std::cout << "Oops: dynamic" << std::endl ;
}
delete array ;
return 0 ;
}

renvoie :

allocated 5 Toto dynamically
Oops: dynamic
Salut
Segmentation fault

Ce n'est pas ce que j'appellerai « marcher ». (À vrai dire, ça
me semblait évident que cet exemple ne pourrait pas marcher.)


D'un autre cote c'est normal vu que tu créés avec new[] et que tu
libères avec delete.
Si local "se voit" Dynamic c'est parce que que le nombre d'elements
est mal calculé.
Mais c'est une erreur de ma part de marquer les objets dans le
tableau, comme créés sur le tas.
Il faut les marquer comme créé sur la pile, vu qu'ils ne seront pas
detruit un par un, mais l'ensemble du tableau à la fois .

dans ton exemple ca devient :
void*
Toto::operator new[](
size_t n )
{
void* result = ::operator new[]( n ) ;
allocCount = 0;
return result ;
}

Merci pour les explications et le temps passé, je vais revoir tout ça
à tête reposée .

En multi-thread c'est aléatoire, ça peut ne pas marcher si un thr ead
cree un tableau sur le tas
tant dis qu'un autre en crée un sur la pile puisqu'ils utilise la m ême
variable
statique.


En somme, ça ne marche pas.


Exact :)



Si j'ai bien compris, le std::set sert à la construction pour verif ier
le valeur de this avec celle du set?
Si on la trouve on peut déduire si l'objet est créé sur le tas ou pas.


Non. Il sert lors du delete. Si on trouve le pointeur dans
l'ensemble, on appelle delete ; sinon, on ne l'appelle pas.


Je suis pas persuadé d'avoir besoin de stocker les adresses des
pointeurs.
Pour savoir si on appelle delete, autant mettre l'info dans l'objet
lors
de sa construction et de disposer de cette info à sa destruction.

Les objets sont détruits par une autre classe, qui va appeller delete
au vu de cette info:
- Si l'objet a été créés avec new , on appelera delete.
- S'il a été crées sans new, pas d'appel à delete.
- S'il fait partie d'un tableau, pas d'appel à delete : il sera
détruit lors de la
destruction du tableau avec le delete[]

D'autre part cette classe ne gère q'un objet à la fois et pas des
tableaux, donc ca simplifie le problème.




Avatar
James Kanze
On Mar 26, 9:09 pm, wrote:
[...]
Ce qui n'est pas forcement juste. Le nombre réel en serait bien
inférieur à nb_elem, jamais strictement supérieur, mais nb_elem
peut bien être strictement supérieur au nombre d'éléments.


Je me suis apercu en faisant des essais que nb_elem = ( bytes - 4 ) /
sizeof(CBase1)
mais c'est peut-être spécifique à mon OS.


Chez moi, c'est bytes - 8. Ça dépend beaucoup du système. (Pas
forcément l'OS, mais le compilateur.)

creation_dyn_tb = true;
void* result = ::operator new [] ( bytes );


Et que se passe-t-il si ::operator new[] lève une exception ?


il manque un throw bad_alloc()


Le throw, il y est, dans ::operator new. En revanche, l'état de
tes variables statiques... Si tout de suite après, tu crées un
objet sur la pile, tu risques de le considérer comme dynamique
plus tard.

Le problème de base, évidemment, c'est l'integrité
transactionnelle de ce que tu fais.

[...]

int
main()
{
Toto* array = new Toto[ 5 ] ;
std::cout << "allocated 5 Toto dynamically" << std::endl ;
for ( int i = 0 ; i < 5 ; ++ i ) {
if ( ! array[ i ].isDym ) {
std::cout << "Oops: not dynamic" << std::endl ;
}
}
Toto local ;
if ( local.isDym ) {
std::cout << "Oops: dynamic" << std::endl ;
}
delete array ;
return 0 ;
}

renvoie :

allocated 5 Toto dynamically
Oops: dynamic
Salut
Segmentation fault

Ce n'est pas ce que j'appellerai « marcher ». (À vrai dire, ça
me semblait évident que cet exemple ne pourrait pas marcher.)


D'un autre cote c'est normal vu que tu créés avec new[] et que tu
libères avec delete.


Je l'ai vu par la suite. C'est effectivement la cause du
« segmentation fault ». Mais ce qui m'intéressait surtout, c'est
le « Oops: dynamic ». J'ai un objet créé sur la pile qui se dit
alloué dynamiquement.

Si local "se voit" Dynamic c'est parce que que le nombre d'elements
est mal calculé.


C'est calculé avec la formule que tu avais posté originellement.

En fait, il n'y a aucune façon de calculer le nombre d'objets de
façon fiable. Il y a des formules qui marche plus ou moins,
selon le compilateur et les phases de la lune, mais c'est tout.

Mais c'est une erreur de ma part de marquer les objets dans le
tableau, comme créés sur le tas.
Il faut les marquer comme créé sur la pile, vu qu'ils ne seront pas
detruit un par un, mais l'ensemble du tableau à la fois .
dans ton exemple ca devient :


Gérer les tableaux, c'est en effet plus compliqué. Parce qu'il
faut savoir quand on peut effacer le tableau complet.

Mais en passant : est-ce que tu as réelement besoin de gérer les
tableau. Moi, dans 15 ans de C++, je ne me suis jamais servi de
new[]. Pas une seule fois. Peut-être suffit-il de déclarer
l'operator new[] privé, sans en donner une implémentation.

(Mais pour répondre à ce genre de question, il faudrait savoir
exactement ce que tu essaies de faire.)

Si j'ai bien compris, le std::set sert à la construction pour verif ier
le valeur de this avec celle du set?
Si on la trouve on peut déduire si l'objet est créé sur le tas ou pas.


Non. Il sert lors du delete. Si on trouve le pointeur dans
l'ensemble, on appelle delete ; sinon, on ne l'appelle pas.


Je suis pas persuadé d'avoir besoin de stocker les adresses des
pointeurs.
Pour savoir si on appelle delete, autant mettre l'info dans l'objet
lors
de sa construction et de disposer de cette info à sa destruction.


Si on peut le faire de façon fiable.

En passant, à la place de tout ce bazar, est-ce qu'il ne serait
pas plus simple de rendre operator new privé, fournir une
fonction usine ? (Ou mieux, simplement allouer toutes les
instances sur la pile.)

Les objets sont détruits par une autre classe, qui va appeller delete
au vu de cette info:
- Si l'objet a été créés avec new , on appelera delete.
- S'il a été crées sans new, pas d'appel à delete.
- S'il fait partie d'un tableau, pas d'appel à delete : il sera
détruit lors de la
destruction du tableau avec le delete[]


Mais la grande question est : pourquoi ? Ça me semble tordu,
mais je ne sais pas pourquoi tu veux le faire.

--
James Kanze (GABI Software) mailto:
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
dug8C
Le throw, il y est, dans ::operator new. En revanche, l'état de
tes variables statiques... Si tout de suite après, tu crées un
objet sur la pile, tu risques de le considérer comme dynamique
plus tard.

Le problème de base, évidemment, c'est l'integrité
transactionnelle de ce que tu fais.

oui j'avais pas vu cet aspect des choses.


Mais c'est une erreur de ma part de marquer les objets dans le
tableau, comme créés sur le tas.
Il faut les marquer comme créé sur la pile, vu qu'ils ne seront pas
detruit un par un, mais l'ensemble du tableau à la fois .
dans ton exemple ca devient :


Gérer les tableaux, c'est en effet plus compliqué. Parce qu'il
faut savoir quand on peut effacer le tableau complet.

Mais en passant : est-ce que tu as réelement besoin de gérer les
tableau. Moi, dans 15 ans de C++, je ne me suis jamais servi de
new[]. Pas une seule fois. Peut-être suffit-il de déclarer
l'operator new[] privé, sans en donner une implémentation.

(Mais pour répondre à ce genre de question, il faudrait savoir
exactement ce que tu essaies de faire.)


Effectivement les tableaux ne sont pas une nécessité.
Je vais déclarer new[] privé sans l'implémenter, pour eviter ce
problème.


Si j'ai bien compris, le std::set sert à la construction pour ver ifier
le valeur de this avec celle du set?
Si on la trouve on peut déduire si l'objet est créé sur le ta s ou pas.
Non. Il sert lors du delete. Si on trouve le pointeur dans

l'ensemble, on appelle delete ; sinon, on ne l'appelle pas.
Je suis pas persuadé d'avoir besoin de stocker les adresses des

pointeurs.
Pour savoir si on appelle delete, autant mettre l'info dans l'objet
lors
de sa construction et de disposer de cette info à sa destruction.


Si on peut le faire de façon fiable.

En passant, à la place de tout ce bazar, est-ce qu'il ne serait
pas plus simple de rendre operator new privé, fournir une
fonction usine ? (Ou mieux, simplement allouer toutes les
instances sur la pile.)
qw<
Les objets sont détruits par une autre classe, qui va appeller delete
au vu de cette info:
- Si l'objet a été créés avec new , on appelera delete.
- S'il a été crées sans new, pas d'appel à delete.
- S'il fait partie d'un tableau, pas d'appel à delete : il sera
détruit lors de la
destruction du tableau avec le delete[]


Mais la grande question est : pourquoi ? Ça me semble tordu,
mais je ne sais pas pourquoi tu veux le faire.


Ma classe Coque qui gere les CBase1* doit savoir si elle doit faire
delete ou pas.
Elle agit comme un pointeur intelligent et détruit l'instance de
CBase1* lorsque
son compteur d'instance CBase1* est à zéro.

Coque p = Coque ( new CBase1 );

le problème que je voulais gérer c'est :
CBase1 Ressource;
Coque p2 = Coque ( &Ressource );
A sa destruction p2 ne doit pas appeler delete sur l'adresse stockée.

A la limite je prefèrerais interdire la création sur la pile et
declarer le constructeur protected pour passer par une classe qui me
créérait des instances de CBase1, mais je pense plus simple de marquer
si l'objet est créée sur le tas.

Merci encore pour les explications.




Avatar
James Kanze
On Mar 28, 12:56 am, wrote:
[...]
Ma classe Coque qui gere les CBase1* doit savoir si elle doit faire
delete ou pas.
Elle agit comme un pointeur intelligent et détruit l'instance de
CBase1* lorsque
son compteur d'instance CBase1* est à zéro.


Il y a alors la solution de boost::shared_ptr : c'est lors de la
création du pointeur initial que l'utilisateur indique qu'il ne
faut pas faire de delete :

Toto t ;
Coque< Toto > p1( &t, non ) ;
Coque< Toto > p2( new Toto, oui ) ;

(Dans le cas de boost::shared_ptr, le « oui » est implicit, et
le « non » prend la forme d'un objet destructeur no-op.)

N'empêche que d'après mon expérience, des objets tombent dans
des catégories bien distinctes, dont les valeurs et les objets
d'entité. Et que les valeurs ne sont pour ainsi dire jamais
allouées dynamiquement, et qu'on n'en prend jamais l'adresse, et
les objets d'entité sont toujours alloués dynamiquement. Le
problème de savoir si un objet donné a été alloué dynamiquement
ne se pose pour ainsi dire jamais.

Mais il y a des exceptions, surtout dans la catégorie des
agents ; dans mes streambuf filtrants, par exemple, je passe
bien un paramètre supplémentaire pour dire au streambuf s'il
faut qu'il fasse un delete ou non dans son destructeur, et que
j'y pense, j'ai un ou deux autres cas où ça serait utile.
Pas avec un comptage de références, mais d'une façon absolue :
je passe la responsibilité du delete à l'objet, ou je ne le
passe pas. (Mais dans les projets neufs, j'utilise le collecteur
de Boehm. Le problème ne se pose donc pas.)

--
James Kanze (GABI Software) mailto:
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
dug8C

Il y a alors la solution de boost::shared_ptr : c'est lors de la
création du pointeur initial que l'utilisateur indique qu'il ne
faut pas faire de delete :

Toto t ;
Coque< Toto > p1( &t, non ) ;
Coque< Toto > p2( new Toto, oui ) ;

(Dans le cas de boost::shared_ptr, le « oui » est implicit, et
le « non » prend la forme d'un objet destructeur no-op.)

Ok c'est vrai je n'y avais pas pensé.


N'empêche que d'après mon expérience, des objets tombent dans
des catégories bien distinctes, dont les valeurs et les objets
d'entité. Et que les valeurs ne sont pour ainsi dire jamais
allouées dynamiquement, et qu'on n'en prend jamais l'adresse, et
les objets d'entité sont toujours alloués dynamiquement. Le
problème de savoir si un objet donné a été alloué dynamiquement
ne se pose pour ainsi dire jamais.

Mais il y a des exceptions, surtout dans la catégorie des
agents ; dans mes streambuf filtrants, par exemple, je passe
bien un paramètre supplémentaire pour dire au streambuf s'il
faut qu'il fasse un delete ou non dans son destructeur, et que
j'y pense, j'ai un ou deux autres cas où ça serait utile.
Pas avec un comptage de références, mais d'une façon absolue :
je passe la responsibilité du delete à l'objet, ou je ne le
passe pas. (Mais dans les projets neufs, j'utilise le collecteur
de Boehm. Le problème ne se pose donc pas.)


Merci encore pour le temps passé :)

1 2 3