Je viens de tomber sur un code qui fait quelque chose comme ça
(enfin... si je ne me suis pas trompé en recopiant et en changeant
les identifiants), et je me demande si cette façon d'appeler une
méthode en déréférençant un pointeur nul est portable. Je précise
que cela compile et s'exécute sans problème apparent avec Visual C++.
Si je puis me permettre, ce test n'a aucun sens AMHA.
partant du principe que l'on ne devrait jamais appeler une méthode depuis un pointeur d'instance null, un tel appel peut être qualifié d'erreur et ce test de non-sens.
parlant du traitement qu'en fait VC98, il donne un code fonctionnel (non académique) mais viable (le "sens" du test étant de permettre des appels 'en aveugle')
if (pInstance) pInstance->foo();
se comportant comme:
rt CInstance::foo(){ if (this){ ... } } pInstance->foo();
je n'ai pas VC2005 sur cette machine, je pourrais regarder lundi, mais je serais curieux de savoir comment d'autres compilo - dont gcc - se comportent.
Ça marche aussi sous g++ (4.0.1). Et sans optimisation, et avec -O3. Apparamment, le compilateur n'exploite pas le fait de savoir que this ne peut pas être null.
-- 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
Sylvain wrote:
Alain Gaillard wrote on 09/09/2006 13:56:
void UneClasse::UneMethode(){
if (this) {
Si je puis me permettre, ce test n'a aucun sens AMHA.
partant du principe que l'on ne devrait jamais appeler une
méthode depuis un pointeur d'instance null, un tel appel peut
être qualifié d'erreur et ce test de non-sens.
parlant du traitement qu'en fait VC98, il donne un code
fonctionnel (non académique) mais viable (le "sens" du test
étant de permettre des appels 'en aveugle')
if (pInstance) pInstance->foo();
se comportant comme:
rt CInstance::foo(){
if (this){
...
}
}
pInstance->foo();
je n'ai pas VC2005 sur cette machine, je pourrais regarder
lundi, mais je serais curieux de savoir comment d'autres
compilo - dont gcc - se comportent.
Ça marche aussi sous g++ (4.0.1). Et sans optimisation, et avec
-O3. Apparamment, le compilateur n'exploite pas le fait de
savoir que this ne peut pas être null.
--
James Kanze (Gabi Software) email: kanze.james@neuf.fr
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
Si je puis me permettre, ce test n'a aucun sens AMHA.
partant du principe que l'on ne devrait jamais appeler une méthode depuis un pointeur d'instance null, un tel appel peut être qualifié d'erreur et ce test de non-sens.
parlant du traitement qu'en fait VC98, il donne un code fonctionnel (non académique) mais viable (le "sens" du test étant de permettre des appels 'en aveugle')
if (pInstance) pInstance->foo();
se comportant comme:
rt CInstance::foo(){ if (this){ ... } } pInstance->foo();
je n'ai pas VC2005 sur cette machine, je pourrais regarder lundi, mais je serais curieux de savoir comment d'autres compilo - dont gcc - se comportent.
Ça marche aussi sous g++ (4.0.1). Et sans optimisation, et avec -O3. Apparamment, le compilateur n'exploite pas le fait de savoir que this ne peut pas être null.
-- 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
Jean-Marc Bourguet
"James Kanze" writes:
Olivier Miakinen wrote:
Je voudrais savoir si ceci est légal :
class UneClasse { public: void UneMethode(); /* plus d'autres méthodes */ }
void UneClasse::UneMethode() { if (this) { /* faire des choses */ } /* sinon ne rien faire */ }
J'ai le souvenir d'avoir vu quelque part un rapport sur un compilateur qui voyant
if (v == NULL) { // on ne modifie pas v } v->f();
concluait que v n'était pas NULL, lors de l'appel, comme v n'était pas modifié dans le if, il n'était pas NULL non plus avant et donc virait complètement le if. C'est le genre de chose qui ne me surprendrait pas d'une passe comme la "value range propagation" de gcc (il n'est d'ailleurs pas impossible que le compilateur en question soit gcc, et que le quelque part soit la liste de gcc, mais je n'ai pas pu reproduire le problème avec les versions que j'ai mais j'ai pu manquer l'option qui va bien ou c'était peut-être une version CVS).
Je ne suis pas sûr que j'aime les compilateurs aussi intelligent, même si formellement ils ont raison.
-- 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
"James Kanze" <kanze.james@neuf.fr> writes:
Olivier Miakinen wrote:
Je voudrais savoir si ceci est légal :
class UneClasse
{
public:
void UneMethode();
/* plus d'autres méthodes */
}
void UneClasse::UneMethode()
{
if (this) {
/* faire des choses */
}
/* sinon ne rien faire */
}
J'ai le souvenir d'avoir vu quelque part un rapport sur un
compilateur qui voyant
if (v == NULL) {
// on ne modifie pas v
}
v->f();
concluait que v n'était pas NULL, lors de l'appel, comme v
n'était pas modifié dans le if, il n'était pas NULL non plus
avant et donc virait complètement le if. C'est le genre de
chose qui ne me surprendrait pas d'une passe comme la "value
range propagation" de gcc (il n'est d'ailleurs pas
impossible que le compilateur en question soit gcc, et que
le quelque part soit la liste de gcc, mais je n'ai pas pu
reproduire le problème avec les versions que j'ai mais j'ai
pu manquer l'option qui va bien ou c'était peut-être une
version CVS).
Je ne suis pas sûr que j'aime les compilateurs aussi
intelligent, même si formellement ils ont raison.
--
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
J'ai le souvenir d'avoir vu quelque part un rapport sur un compilateur qui voyant
if (v == NULL) { // on ne modifie pas v } v->f();
concluait que v n'était pas NULL, lors de l'appel, comme v n'était pas modifié dans le if, il n'était pas NULL non plus avant et donc virait complètement le if. C'est le genre de chose qui ne me surprendrait pas d'une passe comme la "value range propagation" de gcc (il n'est d'ailleurs pas impossible que le compilateur en question soit gcc, et que le quelque part soit la liste de gcc, mais je n'ai pas pu reproduire le problème avec les versions que j'ai mais j'ai pu manquer l'option qui va bien ou c'était peut-être une version CVS).
Je ne suis pas sûr que j'aime les compilateurs aussi intelligent, même si formellement ils ont raison.
-- 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
Fabien LE LEZ
On Sun, 10 Sep 2006 13:44:51 +0200, Jean-Marc Bourguet :
if (v == NULL) {
concluait que v n'était pas NULL, lors de l'appel, comme v n'était pas modifié dans le if, il n'était pas NULL non plus avant et donc virait complètement le if. [...]
Je ne suis pas sûr que j'aime les compilateurs aussi intelligent, même si formellement ils ont raison.
Je m'attendrais à ce qu'un compilateur affiche un avertissement dans pareil cas, du style "Warning: condition is always false". (Il me semble que Borland C++ le fait quand il s'en aperçoit.)
On Sun, 10 Sep 2006 13:44:51 +0200, Jean-Marc Bourguet
<jm@bourguet.org>:
if (v == NULL) {
concluait que v n'était pas NULL, lors de l'appel, comme v
n'était pas modifié dans le if, il n'était pas NULL non plus
avant et donc virait complètement le if.
[...]
Je ne suis pas sûr que j'aime les compilateurs aussi
intelligent, même si formellement ils ont raison.
Je m'attendrais à ce qu'un compilateur affiche un avertissement dans
pareil cas, du style "Warning: condition is always false".
(Il me semble que Borland C++ le fait quand il s'en aperçoit.)
On Sun, 10 Sep 2006 13:44:51 +0200, Jean-Marc Bourguet :
if (v == NULL) {
concluait que v n'était pas NULL, lors de l'appel, comme v n'était pas modifié dans le if, il n'était pas NULL non plus avant et donc virait complètement le if. [...]
Je ne suis pas sûr que j'aime les compilateurs aussi intelligent, même si formellement ils ont raison.
Je m'attendrais à ce qu'un compilateur affiche un avertissement dans pareil cas, du style "Warning: condition is always false". (Il me semble que Borland C++ le fait quand il s'en aperçoit.)
Franck Branjonneau
Jean-Marc Bourguet écrivait:
J'ai le souvenir d'avoir vu quelque part un rapport sur un compilateur qui voyant
if (v == NULL) { // on ne modifie pas v } v->f();
concluait que v n'était pas NULL, lors de l'appel, comme v n'était pas modifié dans le if, il n'était pas NULL non plus avant et donc virait complètement le if.
Tu cherches probablement (info gcc):
`-fdelete-null-pointer-checks' Use global dataflow analysis to identify and eliminate useless checks for null pointers. The compiler assumes that dereferencing a null pointer would have halted the program. If a pointer is checked after it has already been dereferenced, it cannot be null.
In some environments, this assumption is not true, and programs can safely dereference null pointers. Use `-fno-delete-null-pointer-checks' to disable this optimization for programs which depend on that behavior.
Enabled at levels `-O2', `-O3', `-Os'.
-- Franck Branjonneau
Jean-Marc Bourguet <jm@bourguet.org> écrivait:
J'ai le souvenir d'avoir vu quelque part un rapport sur un
compilateur qui voyant
if (v == NULL) {
// on ne modifie pas v
}
v->f();
concluait que v n'était pas NULL, lors de l'appel, comme v
n'était pas modifié dans le if, il n'était pas NULL non plus
avant et donc virait complètement le if.
Tu cherches probablement (info gcc):
`-fdelete-null-pointer-checks'
Use global dataflow analysis to identify and eliminate useless
checks for null pointers. The compiler assumes that dereferencing
a null pointer would have halted the program. If a pointer is
checked after it has already been dereferenced, it cannot be null.
In some environments, this assumption is not true, and programs can
safely dereference null pointers. Use
`-fno-delete-null-pointer-checks' to disable this optimization for
programs which depend on that behavior.
J'ai le souvenir d'avoir vu quelque part un rapport sur un compilateur qui voyant
if (v == NULL) { // on ne modifie pas v } v->f();
concluait que v n'était pas NULL, lors de l'appel, comme v n'était pas modifié dans le if, il n'était pas NULL non plus avant et donc virait complètement le if.
Tu cherches probablement (info gcc):
`-fdelete-null-pointer-checks' Use global dataflow analysis to identify and eliminate useless checks for null pointers. The compiler assumes that dereferencing a null pointer would have halted the program. If a pointer is checked after it has already been dereferenced, it cannot be null.
In some environments, this assumption is not true, and programs can safely dereference null pointers. Use `-fno-delete-null-pointer-checks' to disable this optimization for programs which depend on that behavior.
Enabled at levels `-O2', `-O3', `-Os'.
-- Franck Branjonneau
Jean-Marc Bourguet
Franck Branjonneau writes:
Tu cherches probablement (info gcc):
`-fdelete-null-pointer-checks' Use global dataflow analysis to identify and eliminate useless checks for null pointers. The compiler assumes that dereferencing a null pointer would have halted the program. If a pointer is checked after it has already been dereferenced, it cannot be null.
Quelque chose du genre en effet, à part que l'exemple dont je me souvenais avait un effet *avant* et pas après le déréférencement.
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
Franck Branjonneau <fasbjx@free.fr> writes:
Tu cherches probablement (info gcc):
`-fdelete-null-pointer-checks'
Use global dataflow analysis to identify and eliminate useless
checks for null pointers. The compiler assumes that dereferencing
a null pointer would have halted the program. If a pointer is
checked after it has already been dereferenced, it cannot be null.
Quelque chose du genre en effet, à part que l'exemple dont
je me souvenais avait un effet *avant* et pas après le
déréférencement.
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
`-fdelete-null-pointer-checks' Use global dataflow analysis to identify and eliminate useless checks for null pointers. The compiler assumes that dereferencing a null pointer would have halted the program. If a pointer is checked after it has already been dereferenced, it cannot be null.
Quelque chose du genre en effet, à part que l'exemple dont je me souvenais avait un effet *avant* et pas après le déréférencement.
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
James Kanze
Jean-Marc Bourguet wrote:
"James Kanze" writes:
Olivier Miakinen wrote:
Je voudrais savoir si ceci est légal :
class UneClasse { public: void UneMethode(); /* plus d'autres méthodes */ }
void UneClasse::UneMethode() { if (this) { /* faire des choses */ } /* sinon ne rien faire */ }
J'ai le souvenir d'avoir vu quelque part un rapport sur un compilateur qui voyant
if (v == NULL) { // on ne modifie pas v } v->f();
concluait que v n'était pas NULL, lors de l'appel, comme v n'était pas modifié dans le if, il n'était pas NULL non plus avant et donc virait complètement le if.
Selon la norme, il a tout à fait raison. De l'autre côté, ce genre de décision suppose une certaine analyse, qui pourrait être accompagné d'un avertissement (par exemple, que la condition est toujours fausse). Dans le cas de this, dans une fonction member non-statique, le compilateur n'a pas besoin d'analyse. (N'empèche que là aussi, je trouve qu'un avertissement ne serait pas mal.)
C'est le genre de chose qui ne me surprendrait pas d'une passe comme la "value range propagation" de gcc (il n'est d'ailleurs pas impossible que le compilateur en question soit gcc, et que le quelque part soit la liste de gcc, mais je n'ai pas pu reproduire le problème avec les versions que j'ai mais j'ai pu manquer l'option qui va bien ou c'était peut-être une version CVS).
C'est une technique de l'optimisation assez connue, et assez répandue en général. Qu'il s'applique aussi à la valeur nul d'un pointeur, ça ne m'étonnerait pas du tout. Que le compilateur conclut à un moment que le pointeur ne peut pas être nul, parce qu'il est déréférencé, c'est peut-être moins fréquent.
Je ne suis pas sûr que j'aime les compilateurs aussi intelligent, même si formellement ils ont raison.
C'est difficile à dire. Dans ce cas-ci, je trouve qu'un avertissement ne serait pas mal -- mais il y a d'autres cas des tests toujours vrai ou toujours faux où il me gène plutôt (des cas, par exemple, où le test est toujours vrai si long n'a que 32 bits, mais peut être faux sur des machines 64 bits).
-- 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
Jean-Marc Bourguet wrote:
"James Kanze" <kanze.james@neuf.fr> writes:
Olivier Miakinen wrote:
Je voudrais savoir si ceci est légal :
class UneClasse
{
public:
void UneMethode();
/* plus d'autres méthodes */
}
void UneClasse::UneMethode()
{
if (this) {
/* faire des choses */
}
/* sinon ne rien faire */
}
J'ai le souvenir d'avoir vu quelque part un rapport sur un
compilateur qui voyant
if (v == NULL) {
// on ne modifie pas v
}
v->f();
concluait que v n'était pas NULL, lors de l'appel, comme v
n'était pas modifié dans le if, il n'était pas NULL non plus
avant et donc virait complètement le if.
Selon la norme, il a tout à fait raison. De l'autre côté, ce
genre de décision suppose une certaine analyse, qui pourrait
être accompagné d'un avertissement (par exemple, que la
condition est toujours fausse). Dans le cas de this, dans une
fonction member non-statique, le compilateur n'a pas besoin
d'analyse. (N'empèche que là aussi, je trouve qu'un
avertissement ne serait pas mal.)
C'est le genre de chose qui ne me surprendrait pas d'une passe
comme la "value range propagation" de gcc (il n'est d'ailleurs
pas impossible que le compilateur en question soit gcc, et que
le quelque part soit la liste de gcc, mais je n'ai pas pu
reproduire le problème avec les versions que j'ai mais j'ai pu
manquer l'option qui va bien ou c'était peut-être une version
CVS).
C'est une technique de l'optimisation assez connue, et assez
répandue en général. Qu'il s'applique aussi à la valeur nul d'un
pointeur, ça ne m'étonnerait pas du tout. Que le compilateur
conclut à un moment que le pointeur ne peut pas être nul, parce
qu'il est déréférencé, c'est peut-être moins fréquent.
Je ne suis pas sûr que j'aime les compilateurs aussi
intelligent, même si formellement ils ont raison.
C'est difficile à dire. Dans ce cas-ci, je trouve qu'un
avertissement ne serait pas mal -- mais il y a d'autres cas des
tests toujours vrai ou toujours faux où il me gène plutôt (des
cas, par exemple, où le test est toujours vrai si long n'a que
32 bits, mais peut être faux sur des machines 64 bits).
--
James Kanze (Gabi Software) email: kanze.james@neuf.fr
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
J'ai le souvenir d'avoir vu quelque part un rapport sur un compilateur qui voyant
if (v == NULL) { // on ne modifie pas v } v->f();
concluait que v n'était pas NULL, lors de l'appel, comme v n'était pas modifié dans le if, il n'était pas NULL non plus avant et donc virait complètement le if.
Selon la norme, il a tout à fait raison. De l'autre côté, ce genre de décision suppose une certaine analyse, qui pourrait être accompagné d'un avertissement (par exemple, que la condition est toujours fausse). Dans le cas de this, dans une fonction member non-statique, le compilateur n'a pas besoin d'analyse. (N'empèche que là aussi, je trouve qu'un avertissement ne serait pas mal.)
C'est le genre de chose qui ne me surprendrait pas d'une passe comme la "value range propagation" de gcc (il n'est d'ailleurs pas impossible que le compilateur en question soit gcc, et que le quelque part soit la liste de gcc, mais je n'ai pas pu reproduire le problème avec les versions que j'ai mais j'ai pu manquer l'option qui va bien ou c'était peut-être une version CVS).
C'est une technique de l'optimisation assez connue, et assez répandue en général. Qu'il s'applique aussi à la valeur nul d'un pointeur, ça ne m'étonnerait pas du tout. Que le compilateur conclut à un moment que le pointeur ne peut pas être nul, parce qu'il est déréférencé, c'est peut-être moins fréquent.
Je ne suis pas sûr que j'aime les compilateurs aussi intelligent, même si formellement ils ont raison.
C'est difficile à dire. Dans ce cas-ci, je trouve qu'un avertissement ne serait pas mal -- mais il y a d'autres cas des tests toujours vrai ou toujours faux où il me gène plutôt (des cas, par exemple, où le test est toujours vrai si long n'a que 32 bits, mais peut être faux sur des machines 64 bits).
-- 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
Alain Gaillard
Ce qui n'est pas si évident, c'est que dans le cas d'une fonction statique, la norme exige quand même que l'expression à gauche soit évaluée. Ce qui signifie que même avec une fonction statique, tu as un comportement indéfini.
Incontestablement.
La risque réele, c'est que ça fonctionne en mode débogue, mais pas une fois qu'on active l'optimisation. Le compilateur note que le test ne peut pas échouer, et le supprime.
Oui... si on veut. Ce que tu dis est vrai. Mais on ne devrait pas en arriver là. On part d'un code qui déréférence un pointeur nul. Le comportement est indéfini, donc pour moi le code est mauvais. Le rôle du compilo serait peut être à la rigueur d'émettre un avertissement quand c'est possible. Mais le code ne doit pa sêtre écrit comme ça de toutes façons.
-- Alain
Ce qui n'est pas si évident, c'est que dans le cas d'une
fonction statique, la norme exige quand même que l'expression à
gauche soit évaluée. Ce qui signifie que même avec une fonction
statique, tu as un comportement indéfini.
Incontestablement.
La risque réele, c'est que ça fonctionne en mode débogue, mais
pas une fois qu'on active l'optimisation. Le compilateur note
que le test ne peut pas échouer, et le supprime.
Oui... si on veut. Ce que tu dis est vrai. Mais on ne devrait pas en
arriver là.
On part d'un code qui déréférence un pointeur nul. Le comportement est
indéfini, donc pour moi le code est mauvais.
Le rôle du compilo serait peut être à la rigueur d'émettre un
avertissement quand c'est possible. Mais le code ne doit pa sêtre écrit
comme ça de toutes façons.
Ce qui n'est pas si évident, c'est que dans le cas d'une fonction statique, la norme exige quand même que l'expression à gauche soit évaluée. Ce qui signifie que même avec une fonction statique, tu as un comportement indéfini.
Incontestablement.
La risque réele, c'est que ça fonctionne en mode débogue, mais pas une fois qu'on active l'optimisation. Le compilateur note que le test ne peut pas échouer, et le supprime.
Oui... si on veut. Ce que tu dis est vrai. Mais on ne devrait pas en arriver là. On part d'un code qui déréférence un pointeur nul. Le comportement est indéfini, donc pour moi le code est mauvais. Le rôle du compilo serait peut être à la rigueur d'émettre un avertissement quand c'est possible. Mais le code ne doit pa sêtre écrit comme ça de toutes façons.
-- Alain
Falk Tannhäuser
Olivier Miakinen schrieb:
class UneClasse { public: void UneMethode(); /* plus d'autres méthodes */ };
void UneClasse::UneMethode() { if (this) { /* faire des choses */ } /* sinon ne rien faire */ }
Comme il a déjà été dit, cela donne un comportement indéfini. Pour illustrer qu'il ne s'agit pas d'un problème purement théorique, voici un exemple où le test en question échouera réellement avec GCC :
struct UneClasse { int toto;
void UneMethode() { if(this != 0) toto = 42; } };
struct UneClasseDerivee : public UneClasse { virtual ~UneClasseDerivee() {} };
int main() { UneClasseDerivee* ucd = 0; ucd->UneMethode(); // Donne un "Segmentation fault (core dumped)" ! return 0; }
Explication : Le sous-objet de classe de base ne se trouve pas forcement au début de l'objet complet (surtout en cas d'héritage multiple ou virtuelle, ou - comme ici - parce que la classe dérivée est polymorphique tandis que la classe de base ne l'est pas, et le compilateur place le pointeur sur la v-table au début de l'objet). Par conséquent, l'appel d'une méthode de la classe de base sur un objet de classe dérivée nécessite un ajustement d'adresse - du coup le 'this' dans UneClasse::UneMethode() ci-dessus ne vaut plus NULL.
Par contre, on note que le code suivant, pourtant relativement semblable, est parfaitement légitime :
Ici, le compilateur doit convertir un pointeur NULL sur UneClasseDerivee en pointeur NULL sur UneClasse lors de l'ajustement d'adresse, ce qui nécessite un test supplémentaire (superflu dans l'exemple précédent).
Falk
Olivier Miakinen schrieb:
class UneClasse
{
public:
void UneMethode();
/* plus d'autres méthodes */
};
void UneClasse::UneMethode()
{
if (this) {
/* faire des choses */
}
/* sinon ne rien faire */
}
Comme il a déjà été dit, cela donne un comportement indéfini. Pour
illustrer qu'il ne s'agit pas d'un problème purement théorique, voici un
exemple où le test en question échouera réellement avec GCC :
struct UneClasse
{
int toto;
void UneMethode()
{
if(this != 0)
toto = 42;
}
};
struct UneClasseDerivee : public UneClasse
{
virtual ~UneClasseDerivee() {}
};
int main()
{
UneClasseDerivee* ucd = 0;
ucd->UneMethode(); // Donne un "Segmentation fault (core dumped)" !
return 0;
}
Explication : Le sous-objet de classe de base ne se trouve pas forcement
au début de l'objet complet (surtout en cas d'héritage multiple ou
virtuelle, ou - comme ici - parce que la classe dérivée est
polymorphique tandis que la classe de base ne l'est pas, et le
compilateur place le pointeur sur la v-table au début de l'objet).
Par conséquent, l'appel d'une méthode de la classe de base sur un objet
de classe dérivée nécessite un ajustement d'adresse - du coup le 'this'
dans UneClasse::UneMethode() ci-dessus ne vaut plus NULL.
Par contre, on note que le code suivant, pourtant relativement
semblable, est parfaitement légitime :
Ici, le compilateur doit convertir un pointeur NULL sur UneClasseDerivee
en pointeur NULL sur UneClasse lors de l'ajustement d'adresse, ce qui
nécessite un test supplémentaire (superflu dans l'exemple précédent).
class UneClasse { public: void UneMethode(); /* plus d'autres méthodes */ };
void UneClasse::UneMethode() { if (this) { /* faire des choses */ } /* sinon ne rien faire */ }
Comme il a déjà été dit, cela donne un comportement indéfini. Pour illustrer qu'il ne s'agit pas d'un problème purement théorique, voici un exemple où le test en question échouera réellement avec GCC :
struct UneClasse { int toto;
void UneMethode() { if(this != 0) toto = 42; } };
struct UneClasseDerivee : public UneClasse { virtual ~UneClasseDerivee() {} };
int main() { UneClasseDerivee* ucd = 0; ucd->UneMethode(); // Donne un "Segmentation fault (core dumped)" ! return 0; }
Explication : Le sous-objet de classe de base ne se trouve pas forcement au début de l'objet complet (surtout en cas d'héritage multiple ou virtuelle, ou - comme ici - parce que la classe dérivée est polymorphique tandis que la classe de base ne l'est pas, et le compilateur place le pointeur sur la v-table au début de l'objet). Par conséquent, l'appel d'une méthode de la classe de base sur un objet de classe dérivée nécessite un ajustement d'adresse - du coup le 'this' dans UneClasse::UneMethode() ci-dessus ne vaut plus NULL.
Par contre, on note que le code suivant, pourtant relativement semblable, est parfaitement légitime :
Ici, le compilateur doit convertir un pointeur NULL sur UneClasseDerivee en pointeur NULL sur UneClasse lors de l'ajustement d'adresse, ce qui nécessite un test supplémentaire (superflu dans l'exemple précédent).