Nous avons maintenant C ++ 11 avec de nombreuses nouvelles fonctionnalités. Le nouveau nullptr
est intéressant et déroutant (du moins pour moi).
Eh bien, plus besoin de la méchante macro NULL
.
int* x = nullptr; myclass* obj = nullptr;
Pourtant, je ne comprends pas comment fonctionne nullptr
. Par exemple, l’article de Wikipedia dit:
C ++ 11 corrige cela en introduisant un nouveau mot-clé pour servir de constante de pointeur nul distincte: nullptr. Il est de type nullptr_t , qui est implicitement convertible et comparable à tout type de pointeur ou type de pointeur à membre. Il n’est pas implicitement convertible ou comparable aux types intégraux, sauf pour bool.
Comment est-ce un mot clé et une instance d’un type?
En outre, avez-vous un autre exemple (à côté de celui de Wikipédia) où nullptr
est supérieur au bon ancien 0
?
Comment est-ce un mot clé et une instance d’un type?
Ce n’est pas surprenant. Les deux true
et false
sont des mots-clés et en tant que littéraux, ils ont un type ( bool
). nullptr
est un littéral de type std::nullptr_t
, et c’est une valeur (vous ne pouvez pas prendre l’adresse en utilisant &
).
4.10
propos de la conversion de pointeur dit qu’une valeur de type std::nullptr_t
est une constante de pointeur nul et qu’une constante de pointeur nul intégrale peut être convertie en std::nullptr_t
. La direction opposée n’est pas autorisée. Cela permet de surcharger une fonction pour les pointeurs et les entiers, et de transmettre nullptr
pour sélectionner la version du pointeur. NULL
passez NULL
ou 0
, sélectionnez la version int
confusion.
Un nullptr_t
de nullptr_t
en un type intégral nécessite un nullptr_t
et a la même sémantique qu’une dissortingbution de (void*)0
vers un type intégral (implémentation de mappage définie). Un nullptr_t
ne peut pas convertir nullptr_t
en un type de pointeur. static_cast
à la conversion implicite si possible ou utilisez static_cast
.
La norme exige que sizeof(nullptr_t)
soit sizeof(void*)
.
De nullptr: un pointeur Null de type sécurisé et clair :
Le nouveau mot clé nullptr C ++ 09 désigne une constante de valeur qui sert de littéral de pointeur null universel, remplaçant le littéral 0 mal défini et faiblement typé et la fameuse macro NULL. nullptr met ainsi fin à plus de 30 ans d’embarras, d’ambiguïté et de bugs. Les sections suivantes présentent la fonction nullptr et montrent comment elle peut remédier aux problèmes de NULL et de 0.
Autres références:
template
Lorsque vous avez une fonction qui peut recevoir des pointeurs vers plusieurs types, l’appel avec NULL
est ambigu. La façon dont cela fonctionne maintenant est très complexe en acceptant un int et en supposant que ce soit NULL
.
template class ptr { T* p_; public: ptr(T* p) : p_(p) {} template ptr(U* u) : p_(dynamic_cast(u)) { } // Without this ptr p(NULL) would be ambiguous ptr(int null) : p_(NULL) { assert(null == NULL); } };
En C++11
vous pourriez surcharger nullptr_t
pour que ptr
serait une erreur de compilation plutôt qu’une assertion au moment de l’exécution.
ptr(std::nullptr_t) : p_(nullptr) { }
Alex Allain, expert en C ++, le dit parfaitement ici :
“… imaginez que vous avez les deux déclarations de fonction suivantes:
void func(int n); void func(char *s); func( NULL ); // guess which function gets called?
Bien que la deuxième fonction semble s’appeler – vous êtes, après tout, en train de passer ce qui semble être un pointeur – c’est vraiment la première fonction qui sera appelée! Le problème est que parce que NULL vaut 0 et que 0 est un entier, la première version de func sera appelée à la place. C’est le genre de chose qui, oui, ne se produit pas tout le temps, mais quand cela arrive, c’est extrêmement frustrant et déroutant. Si vous ne connaissiez pas les détails de ce qui se passe, cela pourrait ressembler à un bogue de compilation. Une fonctionnalité de langage qui ressemble à un bogue de compilateur n’est pas ce que vous voulez.
Entrez nullptr. En C ++ 11, nullptr est un nouveau mot-clé qui peut (et devrait!) Être utilisé pour représenter des pointeurs NULL; En d’autres termes, où que vous écriviez NULL auparavant, vous devriez plutôt utiliser nullptr. Ce n’est pas plus clair pour vous, le programmeur (tout le monde sait ce que NULL signifie), mais il est plus explicite pour le compilateur , qui ne verra plus les 0 partout avoir une signification particulière lorsqu’il est utilisé comme pointeur. ”
nullptr
ne peut pas être affecté à un integral type
tel qu’un int mais uniquement un pointer
type; soit un type de pointeur intégré tel que int *ptr
ou un pointeur intelligent tel que std::shared_ptr
Je crois que c’est une distinction importante car NULL
peut toujours être affecté à un integral type
et un pointer
comme NULL
est une macro étendue à 0
qui peut servir à la fois de valeur initiale pour un int
et de pointer
.
En outre, avez-vous un autre exemple (à côté de celui de Wikipédia) où
nullptr
est supérieur au bon ancien 0?
Oui. C’est aussi un exemple réel (simplifié) qui s’est produit dans notre code de production. Il se démarquait seulement parce que gcc était capable d’émettre un avertissement lors de la compilation croisée vers une plate-forme avec une largeur de registre différente (toujours pas sûr de savoir exactement quand la compilation de x86_64 à x86 avertit warning: converting to non-pointer type 'int' from NULL
) :
Considérez ce code (C ++ 03):
#include struct B {}; struct A { operator B*() {return 0;} operator bool() {return true;} }; int main() { A a; B* pb = 0; typedef void* null_ptr_t; null_ptr_t null = 0; std::cout << "(a == pb): " << (a == pb) << std::endl; std::cout << "(a == 0): " << (a == 0) << std::endl; // no warning std::cout << "(a == NULL): " << (a == NULL) << std::endl; // warns sometimes std::cout << "(a == null): " << (a == null) << std::endl; }
Il donne cette sortie:
(a == pb): 1 (a == 0): 0 (a == NULL): 0 (a == null): 1
C’est un mot-clé car le standard le spécifie comme tel. 😉 Selon la dernière version publique (n2914)
2.14.7 Littéraux de pointeur [lex.nullptr]
pointer-literal: nullptr
Le littéral de pointeur est le mot clé
nullptr
. C’est une valeur de typestd::nullptr_t
.
C’est utile car il ne convertit pas implicitement en valeur intégrale.
Eh bien, d’autres langues ont des mots réservés qui sont des instances de types. Python, par exemple:
>>> None = 5 File "", line 1 SyntaxError: assignment to None >>> type(None)
C’est en fait une comparaison assez étroite car None
est généralement utilisé pour quelque chose qui n’a pas été initialisé, mais en même temps, des comparaisons telles que None == 0
sont fausses.
En revanche, dans la version C, NULL == 0
renvoie la valeur IIRC vraie car NULL
est simplement une macro renvoyant 0, qui est toujours une adresse invalide (AFAIK).
Disons que vous avez une fonction (f) qui est surchargée pour prendre à la fois int et char *. Avant C ++ 11, si vous vouliez l’appeler avec un pointeur nul et que vous utilisiez NULL (c.-à-d. La valeur 0), vous appeleriez celui qui est surchargé pour int:
void f(int); void f(char*); void g() { f(0); // Calls f(int). f(NULL); // Equals to f(0). Calls f(int). }
Ce n’est probablement pas ce que vous vouliez. C ++ 11 résout ce problème avec nullptr; Maintenant, vous pouvez écrire ce qui suit:
void g() { f(nullptr); //calls f(char*) }
0 était la seule valeur entière pouvant être utilisée comme initialiseur sans dissortingbution pour les pointeurs: vous ne pouvez pas initialiser les pointeurs avec d’autres valeurs entières sans conversion. Vous pouvez considérer 0 comme étant un dérivé de syntaxe syntaxiquement similaire à un littéral d’entier. Il peut initier n’importe quel pointeur ou entier. Mais étonnamment, vous verrez qu’il n’a pas de type distinct: c’est un int
. Alors, comment se fait-il que 0 puisse initialiser des pointeurs et que 1 ne le soit pas? Une réponse pratique a été que nous avons besoin d’un moyen de définir la valeur null du pointeur et que la conversion implicite directe de int
en pointeur est sujette à erreur. Ainsi, 0 est devenu une véritable bête bizarre hors de l’ère préhistorique. nullptr
été proposé pour être une véritable représentation de constexpr singleton de valeur nulle pour initialiser des pointeurs. Il ne peut pas être utilisé pour initialiser des entiers directement et élimine les ambiguïtés impliquées dans la définition de NULL
en termes de 0. nullptr
pourrait être défini comme une bibliothèque utilisant la syntaxe std mais sémantiquement comme un composant principal manquant. NULL
est maintenant déconseillé en faveur de nullptr
, sauf si une bibliothèque décide de la définir comme nullptr
.
NULL n’a pas besoin d’être 0. Tant que vous utilisez toujours NULL et jamais 0, NULL peut avoir n’importe quelle valeur. En supposant que vous programmez un microcontrôleur von Neuman avec une mémoire plate, ses vektors d’interruption sont à 0. Si NULL est égal à 0 et que quelque chose est écrit à un pointeur NULL, le microcontrôleur se bloque. Si NULL est, disons 1024 et à 1024 il y a une variable réservée, l’écriture ne le plantera pas, et vous pourrez détecter les assignations de pointeur NULL depuis l’intérieur du programme. Ceci est inutile sur les PC, mais pour les sondes spatiales, les équipements militaires ou médicaux, il est important de ne pas tomber en panne.