paulo1205
(usa Ubuntu)
Enviado em 01/05/2016 - 20:22h
Em C não há dúvida: use
NULL . Esse símbolo, definido em <stddef.h>, tem o valor de um ponteiro nulo e o tipo
void * , que funciona mais ou menos com “ponteiro para qualquer tipo”.
Usar o valor inteiro zero era a abordagem preferida pelo C++ antes do padrão de 2011 (e também era a do C antes do padrão de 1989, que introduziu
void ,
void * e padronizou a constante
NULL ).
O sistema de tipos do C++ trata
void * do modo diferente do C. Em vez do sentido de ponteiro para qualquer tipo de dado, o sentido é mais como o de ponteiro para dado de tipo indeterminado. Pode-se fazer com que o ponteiro desses aponte para um dado de tipo conhecido, mas o reverso requer muito mais cuidado. Veja:
char *pc;
int *pi;
void *pv;
pc=strdup("Teste");
pv=pc; // OK em C e C++.
pc=pv; // OK em C, erro em C++.
pi=pv; // OK em C, erro em C++.
free(pc);
Além de atribuições diretas como as acima, C++ tem outro aspecto, inexistente em C, que reforça a necessidade de cuidado com conversão de tipos, que são dois modos de polimorfismo suportado pela linguagem, ambos ligados aos tipos de dados que lhes servem de parâmetros (a saber: a sobrecarga de funções e programação genérica).
E acredito (não li isso em lugar nenhum -- é minha interpretação pessoal, mesmo) que a opção de usar a constante zero em vez de conversões automáticas de um tipo de ponteiro padrão foi uma solução de compromisso: o usuário se livrava de algumas surpresas referentes à conversão automática (ou da falta delas) entre ponteiros, mas podia acabar encontrando outras, por conta da do uso de funções ou objetos parametrizados com inteiros, quando ele talvez esperasse parâmetros ponteiros. Veja abaixo.
// Código em C++98
#include <cstddef> // define NULL, que em C++ é “0” (e não “(void *)0”, como é em C).
void f(char *pc){ /* ... Usa pc. ... */ }
void f(int i){ /* ... Usa i. ... */ }
void g(void *pv){ /* ... Usa pv. ... */ }
void g(int i){ /* ... Usa i. ... */ }
void h(){
char *pc;
void *pv;
int *pi;
pc=0; // OK: pc é um ponteiro para char com valor nulo.
pc=NULL; // OK (em C++, NULL é 0; em C, é ((void *)0)).
pc=(void *)0; // ERRO: tipo de ponteiro incompatível (mesmo sendo o valor nulo).
f(NULL); // OK, mas chama f(int), não f(char *).
f(pc); // OK: chama f(char *);
pv=pc; // OK: conversão para void * é permitida.
pc=pv; // ERRO: tipo de ponteiro incompatível (em C seria permitido).
f(pv); // ERRO: tipo de ponteiro incompatível.
g(pv); // OK: chama g(void *).
g(pc); // OK: chama g(void *).
g(NULL); // OK, mas chama g(int).
pc=(char *)pv; // OK, mas obsoleto.
pc=static_cast<char *>(pv); // OK, desde que pv seja um void * (é o caso).
pi=static_cast<int *>(pv); // OK.
pc=(void *)pi; // OK, mas obsoleto e perigoso.
pc=static_cast<char *>(pi); // ERRO: tipo incompatível.
pc=reinterpret_cast<char *>(pi); // OK: você está dizendo que assume o risco.
}
O tipo
std::nullptr_t foi introduzido no C++11, juntamente com a nova palavra-chave
nullptr .
nullptr é o único valor válido do tipo
std::nullptr_t . Este, por sua vez, é um tipo distinto de qualquer ponteiro e também de qualquer inteiro, mas permite conversão automática para qualquer ponteiro e também para booleano (tipo
bool ).
O artigo sobre o C++11 na Wikipedia (em Inglês) é muito bom, e tem uma seção sobre
nullptr . Os cppreference.com também tem uma boa descrição.