Laurent Deniau writes:Donc B* devient un A* et reste un A*.Donc comment delete retrouve la taille de l'objet a
detruire et surtout en quoi declarer le destructeur
virtual resoud le probleme s'il a le comportement que tu
dis?
C'est le problème de l'implémentation. Je ne crois pas que
le destructeur virtuel ait grand chose à voir avec la taille
à libérere (la technique classique c'est quand même que
l'allocateur connait la taille de ses blocs) mais plus avec
l'exécution du destructeur de la classe la plus dérivée et
au passage à l'operateur delete du bon pointeur en cas
d'héritage multiple.
Mais bon, c'est vrai qu'on peut utiliser des opérateurs
delete qui prennent la taille aussi.
delete a;
peut donc être compilé en qqch du genre (j'ai jamais
travaillé sur un compilateur C++, il y a peut-être des
choses que j'oublie):
__base = a->__get_most_derived_object();
__sz = a->__get_size_of_most_derived_object();
__delete = a->__get_delete_operator();
a->__destructor_in_charge_of_virtual_members(__base);
(*__delete)(__base, __sz);
Appeler un membre virtuel sur this a un comportement defini: la
fonction sera celle de la classe construite ou de l'ancetre la
fournissant (donc c'est un probleme si elle est pure).
Je ne crois pas que tu puisses compiler du code qui aboutisse a un
appel de methode pure.
Laurent Deniau <Laurent.Deniau@cern.ch> writes:
Donc B* devient un A* et reste un A*.
Donc comment delete retrouve la taille de l'objet a
detruire et surtout en quoi declarer le destructeur
virtual resoud le probleme s'il a le comportement que tu
dis?
C'est le problème de l'implémentation. Je ne crois pas que
le destructeur virtuel ait grand chose à voir avec la taille
à libérere (la technique classique c'est quand même que
l'allocateur connait la taille de ses blocs) mais plus avec
l'exécution du destructeur de la classe la plus dérivée et
au passage à l'operateur delete du bon pointeur en cas
d'héritage multiple.
Mais bon, c'est vrai qu'on peut utiliser des opérateurs
delete qui prennent la taille aussi.
delete a;
peut donc être compilé en qqch du genre (j'ai jamais
travaillé sur un compilateur C++, il y a peut-être des
choses que j'oublie):
__base = a->__get_most_derived_object();
__sz = a->__get_size_of_most_derived_object();
__delete = a->__get_delete_operator();
a->__destructor_in_charge_of_virtual_members(__base);
(*__delete)(__base, __sz);
Appeler un membre virtuel sur this a un comportement defini: la
fonction sera celle de la classe construite ou de l'ancetre la
fournissant (donc c'est un probleme si elle est pure).
Je ne crois pas que tu puisses compiler du code qui aboutisse a un
appel de methode pure.
Laurent Deniau writes:Donc B* devient un A* et reste un A*.Donc comment delete retrouve la taille de l'objet a
detruire et surtout en quoi declarer le destructeur
virtual resoud le probleme s'il a le comportement que tu
dis?
C'est le problème de l'implémentation. Je ne crois pas que
le destructeur virtuel ait grand chose à voir avec la taille
à libérere (la technique classique c'est quand même que
l'allocateur connait la taille de ses blocs) mais plus avec
l'exécution du destructeur de la classe la plus dérivée et
au passage à l'operateur delete du bon pointeur en cas
d'héritage multiple.
Mais bon, c'est vrai qu'on peut utiliser des opérateurs
delete qui prennent la taille aussi.
delete a;
peut donc être compilé en qqch du genre (j'ai jamais
travaillé sur un compilateur C++, il y a peut-être des
choses que j'oublie):
__base = a->__get_most_derived_object();
__sz = a->__get_size_of_most_derived_object();
__delete = a->__get_delete_operator();
a->__destructor_in_charge_of_virtual_members(__base);
(*__delete)(__base, __sz);
Appeler un membre virtuel sur this a un comportement defini: la
fonction sera celle de la classe construite ou de l'ancetre la
fournissant (donc c'est un probleme si elle est pure).
Je ne crois pas que tu puisses compiler du code qui aboutisse a un
appel de methode pure.
| Je comprends que l'effet soint indefini quand la fonction n'est
| pas definie, mais si elle l'est ca me choque un peu.
Moi aussi, d'ailleurs cela fait partie de la liste des choses «
undefined behaviour » que je voudrais enlever. Le problème de la
spécification, c'est quand l'appel est indirect -- ce qui forcerait
à mettre l'adresse dans les vtables. Alors au lieu de faire une
règle spéciale, ils ont mis undefined-behaviour.
| Je comprends que l'effet soint indefini quand la fonction n'est
| pas definie, mais si elle l'est ca me choque un peu.
Moi aussi, d'ailleurs cela fait partie de la liste des choses «
undefined behaviour » que je voudrais enlever. Le problème de la
spécification, c'est quand l'appel est indirect -- ce qui forcerait
à mettre l'adresse dans les vtables. Alors au lieu de faire une
règle spéciale, ils ont mis undefined-behaviour.
| Je comprends que l'effet soint indefini quand la fonction n'est
| pas definie, mais si elle l'est ca me choque un peu.
Moi aussi, d'ailleurs cela fait partie de la liste des choses «
undefined behaviour » que je voudrais enlever. Le problème de la
spécification, c'est quand l'appel est indirect -- ce qui forcerait
à mettre l'adresse dans les vtables. Alors au lieu de faire une
règle spéciale, ils ont mis undefined-behaviour.
drkm wrote in message
news:...
[...]En fait, je pensais au fait de normaliser le fait que lorsqu'une
classe définit une fonction membre virtuelle, le compilo déclare
automatiquement virtuel le destructeur. Cela me semblerait
intéressant. Ne serait-ce que pour le destructeur généré par défaut.
Comme il me semble que c'est presqu'obligatoire, sauf cas très
particuliers, je trouve que ce devrait être le comportement par
défaut. Cette proposition a-t-elle déjà été faite ? Et si oui, quels
arguments ont-ils joué contre ?
Je crois qu'on en a parlé. En gros, l'argument contre, c'est qu'on
n'aime pas que l'ajoute et surtout la suppression d'une fonction
virtuelle change la sémantique du destructeur. Je doute que ça ait un
effet dans les programmes réels, mais j'avoue que l'idée ne me plaît pas
particulièrement non plus.
Ce qui aurait pû être mieux, peut-être, c'est que lorsque la classe
définit une fonction membre virtuelle, le compilateur ne génère aucune
des fonctions qu'il génère normalement par défaut. Mais c'est bien trop
tard d'y penser maintenant.
drkm <usenet.fclcxx@fgeorges.org> wrote in message
news:<wkoehr6cb1.fsf@fgeorges.org>...
[...]
En fait, je pensais au fait de normaliser le fait que lorsqu'une
classe définit une fonction membre virtuelle, le compilo déclare
automatiquement virtuel le destructeur. Cela me semblerait
intéressant. Ne serait-ce que pour le destructeur généré par défaut.
Comme il me semble que c'est presqu'obligatoire, sauf cas très
particuliers, je trouve que ce devrait être le comportement par
défaut. Cette proposition a-t-elle déjà été faite ? Et si oui, quels
arguments ont-ils joué contre ?
Je crois qu'on en a parlé. En gros, l'argument contre, c'est qu'on
n'aime pas que l'ajoute et surtout la suppression d'une fonction
virtuelle change la sémantique du destructeur. Je doute que ça ait un
effet dans les programmes réels, mais j'avoue que l'idée ne me plaît pas
particulièrement non plus.
Ce qui aurait pû être mieux, peut-être, c'est que lorsque la classe
définit une fonction membre virtuelle, le compilateur ne génère aucune
des fonctions qu'il génère normalement par défaut. Mais c'est bien trop
tard d'y penser maintenant.
drkm wrote in message
news:...
[...]En fait, je pensais au fait de normaliser le fait que lorsqu'une
classe définit une fonction membre virtuelle, le compilo déclare
automatiquement virtuel le destructeur. Cela me semblerait
intéressant. Ne serait-ce que pour le destructeur généré par défaut.
Comme il me semble que c'est presqu'obligatoire, sauf cas très
particuliers, je trouve que ce devrait être le comportement par
défaut. Cette proposition a-t-elle déjà été faite ? Et si oui, quels
arguments ont-ils joué contre ?
Je crois qu'on en a parlé. En gros, l'argument contre, c'est qu'on
n'aime pas que l'ajoute et surtout la suppression d'une fonction
virtuelle change la sémantique du destructeur. Je doute que ça ait un
effet dans les programmes réels, mais j'avoue que l'idée ne me plaît pas
particulièrement non plus.
Ce qui aurait pû être mieux, peut-être, c'est que lorsque la classe
définit une fonction membre virtuelle, le compilateur ne génère aucune
des fonctions qu'il génère normalement par défaut. Mais c'est bien trop
tard d'y penser maintenant.
Certains compilateurs affectent une adresse spéciale (je crois que GCC
par exemple fera avorter le programme au motif que tu as appelé une
fonction virtuelle pure -- et je crois que c'est quelque chose de
requis par l'ABI commune).
Certains compilateurs affectent une adresse spéciale (je crois que GCC
par exemple fera avorter le programme au motif que tu as appelé une
fonction virtuelle pure -- et je crois que c'est quelque chose de
requis par l'ABI commune).
Certains compilateurs affectent une adresse spéciale (je crois que GCC
par exemple fera avorter le programme au motif que tu as appelé une
fonction virtuelle pure -- et je crois que c'est quelque chose de
requis par l'ABI commune).
Gabriel Dos Reis writes:
#include <iostream>
class A {
public:
A() { g(); }
void g() { f(); }
virtual void f() = 0;
};
void A::f() {
std::cout << "A::fn";
}
class B: public A {
void f() { std::cout << "B::fn";}
};
int main() {
B b;
}
avec qqch comme (message de como)
C++ runtime abort: a pure virtual function was called
mais si on enlève le =0, le programme fonctionne et affiche
A::f
pour les 3 compilateurs. Je suis un peu confu de n'avoir
pas essayé plus tot. Mais bon, j'ai (ré?)appris une
particularité supplémentaire des virtuelles pures.
Gabriel Dos Reis <gdr@integrable-solutions.net> writes:
#include <iostream>
class A {
public:
A() { g(); }
void g() { f(); }
virtual void f() = 0;
};
void A::f() {
std::cout << "A::fn";
}
class B: public A {
void f() { std::cout << "B::fn";}
};
int main() {
B b;
}
avec qqch comme (message de como)
C++ runtime abort: a pure virtual function was called
mais si on enlève le =0, le programme fonctionne et affiche
A::f
pour les 3 compilateurs. Je suis un peu confu de n'avoir
pas essayé plus tot. Mais bon, j'ai (ré?)appris une
particularité supplémentaire des virtuelles pures.
Gabriel Dos Reis writes:
#include <iostream>
class A {
public:
A() { g(); }
void g() { f(); }
virtual void f() = 0;
};
void A::f() {
std::cout << "A::fn";
}
class B: public A {
void f() { std::cout << "B::fn";}
};
int main() {
B b;
}
avec qqch comme (message de como)
C++ runtime abort: a pure virtual function was called
mais si on enlève le =0, le programme fonctionne et affiche
A::f
pour les 3 compilateurs. Je suis un peu confu de n'avoir
pas essayé plus tot. Mais bon, j'ai (ré?)appris une
particularité supplémentaire des virtuelles pures.
"Jean-Marc Bourguet" a écrit dans le message de
news:"K. Ahausse" writes:
Je ne vois pas ce que les virtuelles pures viennent faire ici.
Oups, pardon. Je croyais qu'il était question de l'appel d'une
fonction virtuelle pure (définie dans la classe dérivée) à partir du
constructeur de la classe de base.
Pour citer, James Kanze : "La seule chose à se rappeler, c'est que
pendant l'execution d'un constructeur ou d'un destructeur de la class
C, le type dynamique est C. On ne tient pas compte du futur (dans le
cas d'un constructeur) ..."
Et donc : a la construction de la classe de base, la fonction
virtuelle pure n'étant pas encore définie, on ne peut faire appel à
elle.
Ce sont des membres virtuels comme les autres a deux differences
pres:
- les descendants instanciables sont obliges de les supplanter
- on peut ne pas les definir (a l'exception du destructeur)
Tu confonds peut-etre avec l'autre partie de la discussion avec
Laurent ou j'indique qu'on peut arriver a cause de ces regles sur le
type dynamique durant l'execution des constructeurs et des
destructeurs a appeler un membre virtuel pur sans le faire de
maniere qualifiee (on peut naturellement le faire de maniere
qualifiee dans d'autres contextes).
Là je n'ai pas compris .
"Jean-Marc Bourguet" <jm@bourguet.org> a écrit dans le message de
news:pxbd5y46gjn.fsf@news.bourguet.org...
"K. Ahausse" <anonymous@discussions.microsoft.com> writes:
Je ne vois pas ce que les virtuelles pures viennent faire ici.
Oups, pardon. Je croyais qu'il était question de l'appel d'une
fonction virtuelle pure (définie dans la classe dérivée) à partir du
constructeur de la classe de base.
Pour citer, James Kanze : "La seule chose à se rappeler, c'est que
pendant l'execution d'un constructeur ou d'un destructeur de la class
C, le type dynamique est C. On ne tient pas compte du futur (dans le
cas d'un constructeur) ..."
Et donc : a la construction de la classe de base, la fonction
virtuelle pure n'étant pas encore définie, on ne peut faire appel à
elle.
Ce sont des membres virtuels comme les autres a deux differences
pres:
- les descendants instanciables sont obliges de les supplanter
- on peut ne pas les definir (a l'exception du destructeur)
Tu confonds peut-etre avec l'autre partie de la discussion avec
Laurent ou j'indique qu'on peut arriver a cause de ces regles sur le
type dynamique durant l'execution des constructeurs et des
destructeurs a appeler un membre virtuel pur sans le faire de
maniere qualifiee (on peut naturellement le faire de maniere
qualifiee dans d'autres contextes).
Là je n'ai pas compris .
"Jean-Marc Bourguet" a écrit dans le message de
news:"K. Ahausse" writes:
Je ne vois pas ce que les virtuelles pures viennent faire ici.
Oups, pardon. Je croyais qu'il était question de l'appel d'une
fonction virtuelle pure (définie dans la classe dérivée) à partir du
constructeur de la classe de base.
Pour citer, James Kanze : "La seule chose à se rappeler, c'est que
pendant l'execution d'un constructeur ou d'un destructeur de la class
C, le type dynamique est C. On ne tient pas compte du futur (dans le
cas d'un constructeur) ..."
Et donc : a la construction de la classe de base, la fonction
virtuelle pure n'étant pas encore définie, on ne peut faire appel à
elle.
Ce sont des membres virtuels comme les autres a deux differences
pres:
- les descendants instanciables sont obliges de les supplanter
- on peut ne pas les definir (a l'exception du destructeur)
Tu confonds peut-etre avec l'autre partie de la discussion avec
Laurent ou j'indique qu'on peut arriver a cause de ces regles sur le
type dynamique durant l'execution des constructeurs et des
destructeurs a appeler un membre virtuel pur sans le faire de
maniere qualifiee (on peut naturellement le faire de maniere
qualifiee dans d'autres contextes).
Là je n'ai pas compris .
Gabriel Dos Reis writes:| > #include <iostream>
| > struct A {
| > virtual void f() = 0;
| > virtual ~A() { f(); }
| > };
| > void A::f() { std::cout << "coucou !n"; }
| Oui, je vois, tu as raison aussi :-)
Non. Je n'ai pas raison : la fonction est *définie*, mais le code
invoque un fonctionnement indéfini (la plupart des compilateurs
l'accepteront sur la base du méchanisme d'appel).
Evidemment ils ont ete mettre en 10.4/3 et une clause qui contredit ce
qu'on peut penser en ne regardant que 12.7 (et en particulier 12.7/3).
Je comprends que l'effet soint indefini quand la fonction n'est pas
definie, mais si elle l'est ca me choque un peu.
Gabriel Dos Reis <gdr@integrable-solutions.net> writes:
| > #include <iostream>
| > struct A {
| > virtual void f() = 0;
| > virtual ~A() { f(); }
| > };
| > void A::f() { std::cout << "coucou !n"; }
| Oui, je vois, tu as raison aussi :-)
Non. Je n'ai pas raison : la fonction est *définie*, mais le code
invoque un fonctionnement indéfini (la plupart des compilateurs
l'accepteront sur la base du méchanisme d'appel).
Evidemment ils ont ete mettre en 10.4/3 et une clause qui contredit ce
qu'on peut penser en ne regardant que 12.7 (et en particulier 12.7/3).
Je comprends que l'effet soint indefini quand la fonction n'est pas
definie, mais si elle l'est ca me choque un peu.
Gabriel Dos Reis writes:| > #include <iostream>
| > struct A {
| > virtual void f() = 0;
| > virtual ~A() { f(); }
| > };
| > void A::f() { std::cout << "coucou !n"; }
| Oui, je vois, tu as raison aussi :-)
Non. Je n'ai pas raison : la fonction est *définie*, mais le code
invoque un fonctionnement indéfini (la plupart des compilateurs
l'accepteront sur la base du méchanisme d'appel).
Evidemment ils ont ete mettre en 10.4/3 et une clause qui contredit ce
qu'on peut penser en ne regardant que 12.7 (et en particulier 12.7/3).
Je comprends que l'effet soint indefini quand la fonction n'est pas
definie, mais si elle l'est ca me choque un peu.