c++ - not - ¿Qué es exactamente nullptr?
nullptr vs null (11)
Ahora tenemos C ++ 11 con muchas características nuevas. Una interesante y confusa (al menos para mí) es la nueva nullptr
.
Bueno, ya no es necesario para la desagradable macro NULL
.
int* x = nullptr;
myclass* obj = nullptr;
Aún así, no entiendo cómo funciona nullptr
. Por ejemplo, el artículo de Wikipedia dice:
C ++ 11 corrige esto introduciendo una nueva palabra clave que sirve como una constante de puntero nula distinguida: nullptr. Es de tipo nullptr_t , que es convertible implícitamente y comparable a cualquier tipo de puntero o tipo puntero a miembro. No es implícitamente convertible o comparable a los tipos integrales, excepto para bool.
¿Cómo es una palabra clave y una instancia de un tipo?
Además, ¿tienes otro ejemplo (al lado de Wikipedia) donde nullptr
es superior al buen viejo 0
?
¿Cómo es una palabra clave y una instancia de un tipo?
Esto no es sorprendente. Tanto true
como false
son palabras clave y como literales tienen un tipo ( bool
). nullptr
es un puntero literal de tipo std::nullptr_t
, y es un prvalue (no puede tomar su dirección usando &
).
4.10
sobre la conversión de punteros dice que un prvalue de tipostd::nullptr_t
es una constante de puntero nula, y que una constante de puntero nula integral se puede convertir astd::nullptr_t
. La dirección opuesta no está permitida. Esto permite sobrecargar una función tanto para punteros como para enteros, y pasarnullptr
para seleccionar la versión del puntero. PasarNULL
o0
seleccionaría confusamente la versiónint
.Una
nullptr_t
denullptr_t
a un tipo integral necesita unreinterpret_cast
y tiene la misma semántica que una conversión de(void*)0
a un tipo integral (implementación de mapeo definida). Unreinterpret_cast
no puede convertirnullptr_t
a ningún tipo de puntero. Confíe en la conversión implícita si es posible o utilicestatic_cast
.El Estándar requiere que
sizeof(nullptr_t)
seasizeof(void*)
.
Además, ¿tienes otro ejemplo (al lado de Wikipedia) donde
nullptr
es superior al buen viejo 0?
Sí. También es un ejemplo (simplificado) del mundo real que ocurrió en nuestro código de producción. Solo se destacó porque gcc pudo emitir una advertencia al compilar de forma cruzada a una plataforma con un ancho de registro diferente (todavía no estoy seguro de por qué solo al compilar de forma cruzada de x86_64 a x86, advierte a la warning: converting to non-pointer type ''int'' from NULL
) :
Considere este código (C ++ 03):
#include <iostream>
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;
}
Se obtiene esta salida:
(a == pb): 1
(a == 0): 0
(a == NULL): 0
(a == null): 1
¿Por qué nullptr en C ++ 11? ¿Qué es? ¿Por qué NULL no es suficiente?
El experto en C ++ Alex Allain lo dice perfectamente aquí :
"... imagina que tienes las siguientes dos declaraciones de funciones:
void func(int n);
void func(char *s);
func( NULL ); // guess which function gets called?
Aunque parece que se llamará a la segunda función, después de todo, está pasando lo que parece ser un puntero, ¡es realmente la primera función a la que se llamará! El problema es que debido a que NULL es 0 y 0 es un entero, se llamará a la primera versión de func. Este es el tipo de cosas que, sí, no sucede todo el tiempo, pero cuando sucede, es extremadamente frustrante y confuso. Si no conocía los detalles de lo que está sucediendo, podría parecer un error de compilación. Una característica del lenguaje que se parece a un error del compilador es, bueno, no es algo que quieras.
Introduzca nullptr. En C ++ 11, nullptr es una nueva palabra clave que puede (y debe!) Usarse para representar punteros NULL; en otras palabras, dondequiera que estuvieras escribiendo NULL antes, deberías usar nullptr en su lugar. El programador no lo tiene más claro (todo el mundo sabe lo que significa NULL), pero es más explícito para el compilador , que ya no verá 0s en todas partes para tener un significado especial cuando se usa como puntero ".
0 solía ser el único valor entero que podría usarse como un inicializador sin conversión para los punteros: no puede inicializar los punteros con otros valores enteros sin una conversión. Puede considerar 0 como un consexpr singleton sintácticamente similar a un entero literal. Puede iniciar cualquier puntero o entero. Pero, sorprendentemente, encontrará que no tiene un tipo distinto: es un int
. Entonces, ¿cómo 0 puede inicializar punteros y 1 no puede? Una respuesta práctica fue que necesitamos un medio para definir el valor nulo del puntero y la conversión implícita directa de int
a un puntero es propensa a errores. Así, 0 se convirtió en una verdadera bestia extraña y extraña de la era prehistórica. nullptr
se propuso ser una representación real de singleton constexpr de valor nulo para inicializar punteros. No se puede usar para inicializar directamente enteros y elimina las ambigüedades involucradas con la definición de NULL
en términos de 0. nullptr
podría definirse como una biblioteca que utiliza la sintaxis estándar, pero se considera semánticamente como un componente central faltante. NULL
ahora está en desuso a favor de nullptr
, a menos que alguna biblioteca decida definirlo como nullptr
.
Bueno, otros idiomas tienen palabras reservadas que son instancias de tipos. Python, por ejemplo:
>>> None = 5
File "<stdin>", line 1
SyntaxError: assignment to None
>>> type(None)
<type ''NoneType''>
En realidad, esta es una comparación bastante cercana porque None
se usa normalmente para algo que no se ha inicializado, pero al mismo tiempo las comparaciones como None == 0
son falsas.
Por otro lado, en el plano C, NULL == 0
devolverá el IIRC verdadero porque NULL
es solo una macro que devuelve 0, que siempre es una dirección no válida (AFAIK).
Cuando tiene una función que puede recibir punteros a más de un tipo, llamarla con NULL
es ambiguo. La forma en que se soluciona esto ahora es muy complicada al aceptar un int y suponer que es NULL
.
template <class T>
class ptr {
T* p_;
public:
ptr(T* p) : p_(p) {}
template <class U>
ptr(U* u) : p_(dynamic_cast<T*>(u)) { }
// Without this ptr<T> p(NULL) would be ambiguous
ptr(int null) : p_(NULL) { assert(null == NULL); }
};
En C++11
podría sobrecargarse en nullptr_t
para que ptr<T> p(42);
sería un error en tiempo de compilación en lugar de una aserción en tiempo de ejecución.
ptr(std::nullptr_t) : p_(nullptr) { }
De nullptr: Un puntero nulo de tipo seguro y claro :
La nueva palabra clave nullptr de C ++ 09 designa una constante rvalue que sirve como un literal de puntero nulo universal, que reemplaza el 0 y el literal 0 de tipo débil y la infame macro NULL. nullptr pone fin a más de 30 años de vergüenza, ambigüedad y errores. Las siguientes secciones presentan la facilidad nullptr y muestran cómo puede remediar las enfermedades de NULL y 0.
Otras referencias:
- WikiBooks , con código de ejemplo.
- Aquí en : ¿Utiliza NULL o 0 (cero) para los punteros en C ++?
-
template
- Grupo de Google: comp.lang.c ++. Moderated - discusión del compilador
Digamos que tiene una función (f) que está sobrecargada para tomar tanto int como char *. Antes de C ++ 11, si deseaba llamarlo con un puntero nulo y usó NULL (es decir, el valor 0), debería llamar al sobrecargado para int:
void f(int);
void f(char*);
void g()
{
f(0); // Calls f(int).
f(NULL); // Equals to f(0). Calls f(int).
}
Esto probablemente no es lo que querías. C ++ 11 resuelve esto con nullptr; Ahora puedes escribir lo siguiente:
void g()
{
f(nullptr); //calls f(char*)
}
Es una palabra clave porque el estándar lo especificará como tal. ;-) Según el último borrador público (n2914)
2.14.7 literales de puntero [lex.nullptr]
pointer-literal: nullptr
El puntero literal es la palabra clave
nullptr
. Es un valor de tipostd::nullptr_t
.
Es útil porque no se convierte implícitamente en un valor integral.
NULL no necesita ser 0. Mientras use siempre NULL y nunca 0, NULL puede ser cualquier valor. Suponiendo que programe un Microcontrolador Von Neuman con memoria plana, que tiene sus vectores de interrupción en 0. Si NULL es 0 y algo escribe en un puntero NULL, el Microcontrolador falla. Si NULL es, digamos 1024 y en 1024 hay una variable reservada, la escritura no la bloqueará, y puede detectar asignaciones de puntero NULL desde dentro del programa. Esto no tiene sentido en las PC, pero para las sondas espaciales, el equipo militar o médico es importante no bloquearse.
nullptr
no se puede asignar a un integral type
como un int, sino solo un pointer
tipo; ya sea un tipo de puntero incorporado como int *ptr
o un puntero inteligente como std::shared_ptr<T>
Creo que esta es una distinción importante porque NULL
todavía puede asignarse a un integral type
y un pointer
como NULL
es una macro expandida a 0
que puede servir como valor inicial para un int
así como un pointer
.