168 votes

Quels sont les avantages d'utiliser nullptr?

Ce morceau de code fait conceptuellement la même chose pour les trois pointeurs (initialisation du pointeur sécurisé):

 int* p1 = nullptr;
int* p2 = NULL;
int* p3 = 0;
 

Et alors, quels sont les avantages de l'affectation des pointeurs nullptr rapport à l'attribution des valeurs NULL ou 0 ?

187voto

Nawaz Points 148870

Dans ce code, il ne semble pas être un avantage. Mais voir ces fonctions surchargées:

void f(char const *ptr);
void f(int v);

f(NULL);  //which function will be called?

La fonction qui va être appelée? Bien sûr, l'intention ici est d'appeler f(char const *), mais en réalité, f(int) sera appelé!! C'est un gros problème1, n'est-ce pas?

Donc, la solution à ces problèmes est d'utiliser nullptr:

f(nullptr); //first function is called

Bien sûr, ce n'est pas le seul avantage de l' nullptr. Voici un autre:

template<typename T, T *ptr>
struct something{};                     //primary template

template<>
struct something<nullptr_t, nullptr>{};  //partial specialization for nullptr

Car, dans le modèle, le type d' nullptr est déduite comme nullptr_t, de sorte que vous pouvez écrire ceci:

template<typename T>
void f(T *ptr);   //function to handle non-nullptr argument

void f(nullptr_t); //an overload to handle nullptr argument!!!

1. En C++, NULL est défini comme #define NULL 0, de sorte qu'il est fondamentalement int, c'est pourquoi, f(int) est appelé.

90voto

Alok Save Points 115848

C++11 introduit nullptr, il est connu comme l' Null pointeur constant et améliore la sécurité de type et résout des situations ambiguës à la différence de l'existant mise en œuvre dépend de pointeur null constante NULL. Pour être en mesure de comprendre les avantages de l' nullptr. nous avons d'abord besoin de comprendre qu'est - NULL , et quels sont les problèmes associés.


Qu'est - NULL exactement?

Avant C++11 NULL a été utilisé pour représenter un pointeur qui n'a pas de valeur ou un pointeur qui ne pointe sur rien de valable. Contrairement à l'idée populaire NULL n'est pas un mot-clé en C++. C'est un identificateur défini dans la norme en-têtes de bibliothèque. En bref vous ne pouvez pas utiliser NULL sans, y compris certains de la bibliothèque standard les en-têtes. Considérons l' Exemple de programme:

int main()
{ 
    int *ptr = NULL;
    return 0;
}

Sortie:

prog.cpp: In function 'int main()':
prog.cpp:3:16: error: 'NULL' was not declared in this scope

Le standard C++ définit la valeur NULL comme une mise en œuvre définies macro définie dans la norme de la bibliothèque de fichiers d'en-tête. L'origine de la valeur NULL est à partir de C et de C++ hérité de C. C standard défini comme NULL ou 0 (void *)0. Mais en C++ il y a une différence subtile.
C++ ne pouvait pas accepter cette spécification comme il est. Contrairement au C, C++ est un langage fortement typé.(C ne nécessite pas de cast explicite, tandis que d' void* de tout type, tandis que le C++ mandats un cast explicite) .Cela rend la définition de la valeur NULL spécifié par la norme C inutile dans de nombreux C++ expressions, par exemple:

std::string * str = NULL;         //Case 1
void (A::*ptrFunc) () = &A::doSomething;
if (ptrFunc == NULL) {}           //Case 2

Si la valeur NULL est définie comme (void *)0. Les deux expressions ci-dessus ne fonctionnerait pas.

  • Cas 1: ne compile pas car une automatique cast est nécessaire d' void * de std::string.
  • Cas 2: ne compile pas parce que les acteurs de void * de pointeur de fonction membre est nécessaire.

Donc, contrairement au C, C++ Standard pour mandat de définir la valeur NULL comme littéral numérique 0 ou 0L.


Alors, quel est le besoin pour un autre pointeur null constante lorsque nous avons NULL déjà?

Si C++ comité de normalisation est venu avec une valeur NULL définition qui va travailler pour C++, cette définition a son propre lot de problèmes. NULL suffisamment bien fonctionné pour presque tous les scénarios, mais pas tous. Il a donné surprenant et des résultats erronés pour certains rares scénarios. Par exemple:

#include<iostream>
void doSomething(int)
{
    std::cout<<"In Int version";
}
void doSomething(char *)
{
   std::cout<<"In char* version";
}

int main()
{
    doSomething(NULL);
    return 0;
}

Sortie:

In Int version

Clairement, l'intention semble être d'appeler la version qui prend en char* comme argument, mais comme le résultat montre la fonction qui prend un int version est appelée. C'est parce que la valeur NULL est un littéral numérique.

En outre, Depuis qu'elle est définie par l'implémentation de savoir si la valeur NULL peut être 0 ou 0L il peut y beaucoup de confusion dans la fonction de résolution de surcharge.

Exemple De Programme:

#include <cstddef>

void doSomething(int);
void doSomething(char *);

int main()
{
  doSomething(static_cast <char *>(0));    // Case 1
  doSomething(0);                          // Case 2
  doSomething(NULL)                        // Case 3
}

L'analyse de l'extrait ci-dessus:

  • Cas 1: appels doSomething(char *) comme prévu
  • Cas 2: appels d' doSomething(int) mais peut-être qu' char* version a été à désirer, car 0 EST aussi un pointeur null.
  • Cas 3: Si NULL est défini comme 0,
    appels doSomething(int) quand doSomething(char *) a été destiné, peut-être qui en résulte dans la logique d'erreur lors de l'exécution.
    Tandis que, si NULL est défini comme 0L,
    L'appel est ambigu et les résultats dans d'erreur de compilation.

Donc, en fonction de la mise en œuvre du même code peut donner des résultats divers, ce qui est clairement indésirable. Naturellement, le C++ comité des normes a voulu corriger cela et c'est la première motivation pour nullptr.


Donc, qu'est - nullptr et comment il évite les problèmes d' NULL?

C++11 introduit un nouveau mot clé nullptr pour servir de pointeur null constante. Contrairement à NULL, son comportement n'est pas définie par l'implémentation. Ce n'est pas une Macro, mais il a son propre type. nullptr a le type std::nullptr_t. C++11 de façon appropriée définit les propriétés de l'nullptr pour éviter les inconvénients de NULL. Pour résumer ses propriétés:

Propriété 1: Il possède son propre type std::nullptr_t et
Propriété 2: Il est implicitement convertible et comparable à n'importe quel type de pointeur ou un pointeur de type de membre, mais
Propriété 3: Il n'est pas implicitement convertible ou comparable à celle des types intégraux, sauf pour bool.

Considérons l'exemple suivant:

#include<iostream>
void doSomething(int)
{
    std::cout<<"In Int version";
}
void doSomething(char *)
{
   std::cout<<"In char* version";
}

int main()
{
    char *pc = nullptr;      // Case 1
    int i = nullptr;         // Case 2
    bool flag = nullptr;     // Case 3

    doSomething(nullptr);    // Case 4
    return 0;
}

Dans le programme ci-dessus,

  • Cas 1: OK - Propriété 2
  • Cas 2: Pas Ok - Propriété 3
  • Cas 3: OK - Propriété 3
  • Cas 4: Pas de confusion des Appels - char * version, Propriété 2 & 3

Ainsi, l'introduction de nullptr évite tous les problèmes de la bonne vieille NULL.

Où et comment devriez-vous utiliser nullptr?

La règle de base de C++11 est tout simplement commencer à utiliser nullptr chaque fois que vous avez utilisée d'une autre manière NULL dans le passé.


Référence:

C++11 Standard: C. 3.2.4 Macro NULL
C++11 Standard: 18.2 Types
C++11 Standard: 4.10 Pointeur de conversions
Standard C99: 6.3.2.3 Pointeurs

24voto

Puppy Points 90818

La vraie motivation ici est le transfert parfait.

Considérer:

void f(int* p);
template<typename T> void forward(T&& t) {
    f(std::forward<T>(t));
}
int main() {
    forward(0); // FAIL
}

Il suffit de mettre, 0 est un spécial valeur, mais les valeurs ne peuvent pas se propager à travers le système uniquement les types de. Des fonctions de transfert sont essentiels, et 0 ne peut pas traiter avec eux. Ainsi, il est absolument nécessaire d'introduire nullptr, où le type est ce qui est spécial, et le type peut en effet se propager. En fait, la MSVC équipe a dû introduire nullptr d'avance sur le calendrier après leur mise en œuvre références rvalue et puis découvre ce piège pour eux-mêmes.

Il y a quelques autres cas particuliers où nullptr peut rendre la vie plus facile - mais ce n'est pas une base de cas, comme une fonte de résoudre ces problèmes. Envisager

void f(int);
void f(int*);
int main() { f(0); f(nullptr); }

Les appels de deux surcharges. En outre, prendre en compte

void f(int*);
void f(long*);
int main() { f(0); }

C'est ambigu. Mais, avec nullptr, vous pouvez fournir

void f(std::nullptr_t)
int main() { f(nullptr); }

4voto

iammilind Points 29275

Il n'y a aucun avantage direct de disposer d' nullptr dans la façon dont vous l'ont montré les exemples.
Mais imaginons une situation où vous avez 2 fonctions avec le même nom; 1 int et une autre un int*

void foo(int);
void foo(int*);

Si vous souhaitez appeler l' foo(int*) en passant d'une valeur NULL, alors la voie est:

foo((int*)0); // note: foo(NULL) means foo(0)

nullptr le rend plus facile et intuitive:

foo(nullptr);

Lien supplémentaire de Bjarne de la page web.
Sans intérêt mais sur C++11 note de côté:

auto p = 0; // makes auto as int
auto p = nullptr; // makes auto as decltype(nullptr)

4voto

Angew Points 53063

Comme d'autres l'ont déjà dit, son principal avantage réside dans les surcharges. Et bien que les surcharges explicites de int rapport à un pointeur puissent être rares, considérez des fonctions de bibliothèque standard telles que std::fill (qui m’a mordu plus d’une fois dans C ++ 03):

 MyClass *arr[4];
std::fill_n(arr, 4, NULL);
 

Ne compile pas: Cannot convert int to MyClass* .

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X