OVH Cloud OVH Cloud

Midterm mailing

74 réponses
Avatar
Gabriel Dos Reis
http://www.open-std.org/JTC1/SC22/WG21/docs/papers/2005/#mailing2005-06

-- Gaby

10 réponses

4 5 6 7 8
Avatar
kanze
Arnaud Debaene wrote:
wrote:
wrote:

[...]

Au fait, comment le GC sait-il qu'un objet peut être détruit ?


Ca, c'est le problème de l'implémentation, mais
généralement ca entraine certaines contraintes, par exemple
l'interdiction de faire de l'arithmetique de pointeur
(ceci-dit, je ne suis pas sûr que ce soit un mal
d'interdire cela! ;-)


D'où est-ce que tu as ça ? Parce que les collecteurs que je
connais permettent tous l'arithmétique sur des pointeurs.
(On aurait du mal à l'intérdire, étant donné la définition
de l'opérateur [].)


Pas toute l'arithmetique de pointeur : typiquement, les casts
depuis/vers void* sont interdits il me semble.


Pas du tout. Les seules choses actuellement légales qui seront
interdites, c'est de cacher les pointeurs -- memcpy le pointeur
dans un char[], par exemple, puis l'encrypter.

Ensuite pour utiliser l'opérateur [], si le GC fournit un
compactage du tas (et s'il peut donc déplacer les objets en
mémoire), il faut généralement "pinner" l'objet que l'on
souhaite déréférencer pendant que l'on effectue ses
opérations.


Pour l'instant, je ne connais pas de GC pour C/C++ qui déplace
les objets. Le compactage pose trop de problèmes, pour
l'instant. (Note bien, par exemple, que dans une union
pointer/int, il faudrait savoir si le pointeur ou l'int a été le
dernier affecté.)

--
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 11 Jul 2005 01:02:15 -0700, :

[delete]
Du fait qu'elle sert à la fois à la durée de vie et à la
gestion de la mémoire, il s'avère qu'il faut l'appeler même
dans les cas (assez fréquents dans mon code, au moins) où
l'objet n'a rien de particulier à faire en fin de vie. C'est
du travail (et une source d'erreurs) en plus.


Il y a un point qui me gêne dans ce que tu dis : si l'objet
n'est pas un type de base, tu ne peux pas savoir si le
destructeur est vide ou pas (c'est un détail d'implémentation,
qui peut changer en cours de route). Donc, tu dois l'appeler
systématiquement, "au cas où".

Et franchement, si je suis responsable de la création de la
classe _et_ du code qui l'utilise, je préférerais encore
m'encombrer de quelques delete que de m'engager à ce que le
destructeur reste vide.


En quoi est-ce que ça change quelque chose ? Tu ne peux pas
faire n'importe quoi dans une classe dérivée. La classe de base
définit toujours un contrat, que la classe dérivée doit
respecter. Je ne vois pas où le fait qu'on n'appelera pas les
destructeurs change quoique ce soit.

Dans la pratique : est-ce que tu peux donner un exemple où
certaines classes dérivées pourraient avoir besoin d'un
destructeur, et que ce fait ne soit pas connu à l'auteur de la
classe de base ?

--
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
Anthony Fleury wrote:
Merci beaucoup pour ces réponses toujours aussi détaillées !


Pour les objets dynamiques, le C++ a une syntaxe
particulière, delete, qui s'occupe *aussi* de la gestion de
la mémoire. Du fait qu'elle sert à la fois à la durée de vie
et à la gestion de la mémoire, il s'avère qu'il faut
l'appeler même dans les cas (assez fréquents dans mon code,
au moins) où l'objet n'a rien de particulier à faire en fin
de vie. C'est du travail (et une source d'erreurs) en plus.


Oui, mais pour moi le GC justement se serait chargé, lorsque
l'objet serait devenu "inutile", de récupérer la mémoire, mais
après avoir appelé le destructeur. En fait, il jouait le rôle
du delete que l'on appelle au moment où l'on se rend compte
que notre variable devient inutile.


Ce n'est pas ce qui a été proposé. En fait, le collecteur Boehm
permet bien l'appel des destructeurs lorsqu'un objet est glané.
Ou non, selon la configuration. Mais je crois qu'actuellement,
c'est pour des raisons historiques -- quand ils ont implémenté
le glaneur au départ, on ne savait pas quelle politique il
fallait prendre vis-à-vis des destructeurs.

Aujourd'hui, en tout cas, on sait qu'appeler en guise de
finalisation une fonction conçue pour servir comme un
destructeur C++ serait une erreur. Un destructeur C++ s'exécute
dans un environement bien défini ; une fonction de finalisation
dans un environement tout à fait différent. Certaines choses
qu'on ferait habituellement dans un destructeur ne marchera pas
dans une fonction de finalisation, et vice versa. (Voir par
exemple "Deterministic Destruction can be a Bug",
http://www.hpl.hp.com/personal/Hans_Boehm/gc/det_destr.html. Et
pour un analyse plus approfondi des différences, "Destructors,
Finalizers and Synchronization",
portal.acm.org/citation.cfm?doid`4131.604153.)

[...]
En fait, tous les destructeurs triviaux et les destructeurs se
chargeant juste de faire des libérations d'objets alloués à
l'intérieur de la classe en gros deviendraient obselètes, et
permettraient donc un moins grand nombre de fuites mémoire.
J'ai bien compris ?


Moins de fuites de mémoire, peut-être -- mais il n'y en a pas
dans mes programmes actuellement:-). Beaucoup moins de travail
de ma part pour y arriver, en revanche. Vraiment beaucoup.

et pour toutes ces types, on ferait l'économie du delete. Bref
je vais me plonger dans la FAQ pour comprendre toutes ces
subtilitées.


Il y a un deuxième avantage qu'a cité Andrei Alexandrescu -- la
sécurité de typage. Au départ, je crois qu'il exagérait un peu,
mais plus j'y pense, plus je pense qu'il a raison. Avec le GC,
si tu as un T* qui a été correctement initialisé, tu sais qu'il
pointe à un T. Peut-être un T mort et inutilisable, mais un T
quand même (et c'est possible d'y ajouter des drappeau dans le T
pour detecter avec 100% de sécurité une utilisation s'il est
mort). Avec un delete manuel, il y a toujours la possibilité que
la mémoire a été réallouée, et que ton T* pointe en fait à un U.
Et que ton drappeau d'état a été écrasé par le constructeur de
l'U.

--
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
Fabien LE LEZ
On 12 Jul 2005 00:15:38 -0700, :

Tu ne peux pas
faire n'importe quoi dans une classe dérivée.


Je ne parle pas de classes dérivées, je parle de code qui utilise la
classe.

Si une classe quelconque a un destructeur vide dans sa version 1.0.0,
qu'est-ce qui me prouve que son destructeur sera non trivial dans sa
version 1.0.1 ?


// c.h, version 1.0.0

class C
{
...
public: ~C() {}
...
};


// main.cpp

void f()
{
C* ptr= new C;
...
//Ici, pas de delete, je laisse le GC s'en occuper
}




// c.h, version 1.0.1

class C
{
public:
~C() { CloseHandle (machin); }
private:
HANDLE machin;
};

(La fonction "CloseHandle()" de l'API Win32 est une fonction
fourre-tout capable de fermer des fichiers, libérer des ressources
système, etc.)

Avatar
Michel Michaud
Dans le message 42d2cfa8$0$4961$,
Je me suis mal exprimé. L'appel automatique au destructeur en
C++/CLI ne s'effectue effectivement que sur les variables ayant une
portée locale (alloués sur la pile)


Non, seulement ce qui ressemble à ça : les objets sont déclarés
comme sur la pile, mais le compilateur fait un new et un delete
(ou équivalent pour appeler le destructeur) à la sortie du bloc.

--
Michel Michaud
http://www.gdzid.com
FAQ de fr.comp.lang.c++ :
http://www.cmla.ens-cachan.fr/~dosreis/C++/FAQ/

Avatar
Arnaud Debaene
Michel Michaud wrote:
Dans le message 42d2cfa8$0$4961$,
Je me suis mal exprimé. L'appel automatique au destructeur en
C++/CLI ne s'effectue effectivement que sur les variables ayant une
portée locale (alloués sur la pile)


Non, seulement ce qui ressemble à ça : les objets sont déclarés
comme sur la pile, mais le compilateur fait un new et un delete
(ou équivalent pour appeler le destructeur) à la sortie du bloc.
Oui, on est d'accord : syntaxiquemeent, c'est une variable locale, mais le

code généré contient une allocation dynamique et un try/__finally, avec
"destruction" (appel à Dispose) sur l'objet dans le __finally. Pour
l'utiliateur final, ca revient cependant au même.

Arnaud


Avatar
kanze
Fabien LE LEZ wrote:
On 12 Jul 2005 00:15:38 -0700, :

Tu ne peux pas faire n'importe quoi dans une classe dérivée.


Je ne parle pas de classes dérivées, je parle de code qui
utilise la classe.


Ça revient au même. Une classe a un contrat, qu'il faut
respecter.

Si une classe quelconque a un destructeur vide dans sa version
1.0.0, qu'est-ce qui me prouve que son destructeur sera non
trivial dans sa version 1.0.1 ?


Son contrat.

[...]
(La fonction "CloseHandle()" de l'API Win32 est une fonction
fourre-tout capable de fermer des fichiers, libérer des
ressources système, etc.)


En somme, une fonction virtuelle:-).

--
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
Stan
a écrit dans le message de news:

(La fonction "CloseHandle()" de l'API Win32 est une fonction
fourre-tout capable de fermer des fichiers, libérer des
ressources système, etc.)


En somme, une fonction virtuelle:-).



Tu me fais peur.
Et si M$ avait déposé un brevet sur la virtualité des fonctions ?
;-)


--
-Stan


Avatar
kanze
Stan ( remove the dots ) wrote:
a écrit dans le message de news:

(La fonction "CloseHandle()" de l'API Win32 est une
fonction fourre-tout capable de fermer des fichiers,
libérer des ressources système, etc.)


En somme, une fonction virtuelle:-).


Tu me fais peur.
Et si M$ avait déposé un brevet sur la virtualité des fonctions ?
;-)


Ils en auraient du mal. L'OS que j'ai écrit pour le 8086, en
1979, avait aussi des fonctions « fourre-toute », dont le
comportement exact dépendait du type de l'objet. Alors, c'est à
moi, ce brevêt, et je dois toucher quelque chose pour chaque
license de Windows:-).

En fait, j'ai rarement vu un OS sans l'équivalent des fonctions
virtuelles. Quand j'écris dans une pipe, le comportement est
bien différent que quand j'écris à un socket ou dans un fichier.
Autre fois, évidemment, il était question plutôt d'écrire dans
un fichier ou d'écrire vers l'imprimante ou vers un perforateur
de ruban, mais le principe était le 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
Stan
a écrit dans le message de news:

Stan ( remove the dots ) wrote:
a écrit dans le message de news:

(La fonction "CloseHandle()" de l'API Win32 est une
fonction fourre-tout capable de fermer des fichiers,
libérer des ressources système, etc.)


En somme, une fonction virtuelle:-).


Tu me fais peur.
Et si M$ avait déposé un brevet sur la virtualité des fonctions ?
;-)

Ils en auraient du mal. L'OS que j'ai écrit pour le 8086, en
1979, avait aussi des fonctions « fourre-toute », dont le



De quel OS s'agit-il ?

--
-Stan



4 5 6 7 8