2010-07-15 12 views
7

je suis tombé sur le code suivant récemment:Est-il légal/bien défini C++ d'appeler une méthode non statique qui n'accède pas aux membres via un pointeur nul?

class Foo 
{ 
public: 
    void bar(); 
    // .. other stuff 
}; 

void Foo::bar() 
{ 
    if(!this) { 
     // .. do some stuff without accessing any data members 
     return; 
    } 

    // .. do normal actions using data members 
} 

Le code compile car en C++ méthodes ne sont que des fonctions qui sont implicitement transmis un pointeur pour « ce » et « ce » peut être vérifié pour être NULL comme tout autre pointeur. De toute évidence, ce code est source de confusion et de mauvaise pratique, même s'il ne plante pas; Il serait assez déroutant de parcourir le code dans le débogueur, voir un pointeur NULL est sur le point d'avoir une méthode appelée et ensuite ne pas voir le plantage attendu. Ma question est: est-ce que cela viole la norme C++ pour appeler SomeFooPtr->bar()SomeFooPtr == NULL? Il me vient à l'esprit que cela ne peut pas parce que l'opérateur défini par l'utilisateur -> renvoie un pointeur, ce qui signifie que même si ce pointeur est NULL, il n'a certainement pas été déréférencé (déréférencement d'un pointeur NULL par la norme comme illégale ou indéfinie). D'autre part, la sémantique des pointeurs bruts ne doit pas nécessairement correspondre à la sémantique des pointeurs définis par l'utilisateur - peut-être que l'opérateur-> est considéré comme un déréférencement même si le compilateur n'en génère pas.

+0

Dupliquer: http://stackoverflow.com/questions/2474018/when-does-invoking-a-member-function-on-a-null-instance-result-in-undefined-behav – GManNickG

Répondre

13

Cela fonctionnera probablement sur la plupart des systèmes, mais il s'agit d'un comportement non défini. Quoth Standard:

5.2.5.3

Si E1 a le type « pointeur vers la classe X, » alors l'expression E1->E2 est convertie en forme équivalente (*(E1)).E2 [...]

Et

:

5.2.5.1

Une expression de suffixe suivie d'un point . ou une flèche ->, éventuellement suivie par le mot-clé template (14.8.1), puis suivie d'une expression-id , est une expression postfixe. L'expression postfixe avant le point ou la flèche est évaluée; 58) [...]

58) Cette évaluation se produit même si le résultat est pas nécessaire de déterminer la valeur de l'expression entière de Postfix, par exemple, si l'ID expression désigne un élément statique.

Évaluation des *xx est un résultat de pointeur nul dans le comportement non défini, si le vôtre est clairement un cas d'UB, avant que la fonction est même entrée.

+0

Merci, c'est exactement ce que je cherchais. –

+0

Désolé pour les modifications malpropres. J'ai d'abord négligé quelque chose dans la norme, puis j'ai mal interprété votre question. Ça devrait aller maintenant. – Thomas

+0

Pourriez-vous donner un exemple d'expression de la forme 'A.template B'? Où cette syntaxe est-elle pertinente? – Philipp

1

Peu importe que ce soit légal, il déroute le lecteur. Dans l'implémentation où ce code fonctionne, le vtable est accédé par type, certainement pas par objet.

De plus, je m'attends à ce que ce code ait été mis à couvert pour une défaillance du constructeur, ce qui masquerait une variété de problèmes ailleurs. L'échec du constructeur devrait être géré correctement, pas avec un méchant kludge comme l'exemple.

1

Ceci est (tous ensemble maintenant) comportement indéfini. Cependant, pour de nombreux compilateurs, cela fonctionnera, avec la contrainte supplémentaire que la méthode doit être non virtuelle.

1

C'est UB. Un bon moyen de faire planter est de l'utiliser comme une classe de base d'une classe dérivée qui utilise l'héritage multiple. YMMV.

7

Ce test est interrompu même si le déréférencement n'était pas un UB. Il se casse quand ces ajustements pour l'héritage multiple entrent dans le jeu:

#include <stdio.h> 
class B 
{ 
    int value; 
    public: 
    void foo() 
    { 
     if (!this) 
      printf("this==0\n"); 
     else 
      printf("safe\n"); 
    } 
}; 
class A { public: int anotherValue; }; 
class Z : public A,public B {}; 

int main() 
{ 
    Z *z=0; 
    z->foo(); 
} 

imprime "sûr" ici.

+0

Excellent exemple! Merci. – Vlad