2010-01-20 21 views
27

He trabajado en su mayoría sólo con C y estoy corriendo en algunas cuestiones desconocidas en C++.Comprobación de un objeto nulo en C++

Digamos que tengo alguna función como esta en C, lo que sería muy típico:

int some_c_function(const char* var) 
{ 
    if (var == NULL) { 
     /* Exit early so we don't dereference a null pointer */ 
    } 
    /* The rest of the code */ 
} 

Y digamos que estoy tratando de escribir una función similar en C++:

int some_cpp_function(const some_object& str) 
{ 
    if (str == NULL) // This doesn't compile, probably because some_object doesn't overload the == operator 

    if (&str == NULL) // This compiles, but it doesn't work, and does this even mean anything? 
} 

Básicamente, lo único que trato de hacer es evitar que el programa falle cuando se llama a some_cpp_function() con NULL.

  • ¿Cuál es la forma más típica/común de hacer esto con un objeto de C++ (que no implica la sobrecarga del operador ==)?

  • ¿Es este el enfoque correcto? Es decir, ¿no debería escribir funciones que toman un objeto como argumento, sino escribir funciones de miembro? (pero incluso si es así, responda la pregunta original)

  • Entre una función que toma una referencia a un objeto, o una función que toma un puntero C-style a un objeto, hay razones para elegir una sobre el ¿otro?

+1

Esta es una buena pregunta. Es una pena que la mayoría de las personas que responden no entienden lo que estás preguntando. Yo * creo * Nilesh proporcionó la respuesta correcta; y estoy bastante seguro de que funciona incluso al cruzar un límite de aplicación/objeto compartido * si * el objeto compartido exporta el objeto nulo. – jww

Respuesta

31

Básicamente, todo lo que estoy tratando de hacer es impedir que el programa se estrelle cuando some_cpp_function() se llama con NULL.

No es posible llamar a la función con NULL. Uno de los propósitos de tener la referencia, apunta a algún objeto siempre, ya que debes inicializarlo al definirlo. No crea que la referencia sea un puntero elegante, piense en ello como un nombre de alias para el objeto en sí. Entonces este tipo de confusión no surgirá.

+1

No es precisamente el propósito principal (e incluso el principal) de tener referencias. –

+0

En realidad, es posible llamar a 'some_cpp_function (NULL)', si 'some_object' tiene un constructor no explícito que toma un puntero (o cualquier cosa que se pueda convertir implícitamente de 0). Si, por ejemplo, 'some_object' es un typedef para' std :: string', el programa se bloqueará en el constructor del objeto 'std :: string' temporal, pero eso ocurre fuera de' some_cpp_function'. – dalle

+1

Es posible crear una "referencia NULL" desreferenciando un puntero NULL (lo que conduce a un comportamiento indefinido). Supongo que esto podría suceder en la práctica, pero entonces es un error grave en el código y debería tratarse de manera más radical, por ejemplo, usando assert. – visitor

2

Debe usar NULL solo con punteros. Su función acepta una referencia y no pueden ser NULL.

Escriba su función al igual que se escribirían en C.

+0

Creo que te perdiste el sentido de la pregunta. En lugar de enfocarse en el primer bloque de código y un puntero de carácter 'NULL', vuelva al título: *** buscando un objeto nulo en C++ *** y el segundo bloque de código. Es decir, ¿cómo se puede significar un objeto proverbialmente nulo utilizado como argumento opcional? – jww

+0

@jww No te entiendo. Dije que las referencias no pueden ser nulas y para usar su función original, pasando un puntero a some_type, si él quiere que sea opcional. –

2

C++ referencias, naturalmente, no pueden ser nulos, no es necesario el cheque. La función solo puede invocarse pasando una referencia a un objeto existente.

+0

Creo que te perdiste el sentido de la pregunta. En lugar de enfocarse en el primer bloque de código y un puntero de carácter 'NULL', vuelva al título: *** buscando un objeto nulo en C++ *** y el segundo bloque de código. Es decir, ¿cómo se puede significar un objeto proverbialmente nulo utilizado como argumento opcional? – jww

+0

@jww Mi respuesta es exactamente sobre el segundo bloque de código. – sharptooth

13

Una referencia no puede ser NULA. La interfaz te hace pasar un objeto real a la función.

lo que no hay necesidad de probar para NULL. Esta es una de las razones por las que las referencias se introdujeron en C++.

Nota todavía se puede escribir una función que toma un puntero. En esta situación, aún debe probar NULL. Si el valor es NULL, regresa temprano como en C. Nota: No debe usar excepciones cuando un puntero es NULL. Si un parámetro nunca debe ser NULL, debe crear una interfaz que use una referencia.

+0

+1, especialmente en las excepciones que no arrojan si un puntero es NULL. –

+2

A veces es apropiado que una interfaz tome un puntero que no sea NULL y no una referencia. Por ejemplo, si la interfaz tomará posesión del objeto al que se apunta. – Omnifarious

+0

O cuando se trata de cadenas C, como std :: string requiere punteros no nulos en varios lugares. –

5

referencia A C++ no es un puntero ni una referencia de estilo # Java/C y no puede ser NULL. Se comportan como si fueran un alias de otro objeto existente.

En algunos casos, si hay errores en su código, puede obtener una referencia en un objeto ya muerto o inexistente, pero lo mejor que puede hacer es esperar que el programa muera lo suficientemente pronto como para poder depurar qué sucedió y por qué su programa se corrompió.

Es decir, he visto la comprobación de código para 'referencias nulas' haciendo algo como: if (&reference == 0), pero el estándar es claro de que no puede haber referencias nulas en un programa bien formado. Si una referencia está vinculada a un objeto nulo, el programa está mal formado y debe corregirse. Si necesita valores opcionales, use punteros (o alguna construcción de nivel superior como boost::optional), no referencias.

+0

+1 por ser la única respuesta que menciona referencias (rotas) a los objetos del montón ya eliminados, etc. – Christian

4

Como todos dijeron, las referencias no pueden ser nulas. Eso es porque, una referencia se refiere a un objeto. En su código:

// this compiles, but doesn't work, and does this even mean anything? 
if (&str == NULL) 

que están tomando la dirección del objeto str. Por definición, existe str, por lo que tiene una dirección. Por lo tanto, no puede ser NULL. Entonces, sintácticamente, lo anterior es correcto, pero lógicamente, la condición if siempre será falsa.

Acerca de sus preguntas: depende de lo que quiera hacer. ¿Desea que la función pueda modificar el argumento? Si es así, pase una referencia. Si no, no lo haga (o pase la referencia al const). Ver this C++ FAQ para algunos buenos detalles.

En general, en C++, la mayoría de las personas prefiere pasar por referencia al pasar un puntero. Una de las razones es exactamente lo que descubrió: una referencia no puede ser NULL, evitando así el dolor de cabeza de buscarla en la función.

+0

En algunos lugares, leerá que puede obtener una referencia nula: 'type & null = * (type *) 0;', pero desreferenciar el puntero nulo es incorrecto de acuerdo con el estándar, que establece explícitamente que no puede haber referencias nulas dentro de un programa bien formado. –

+0

@dribeas: Inadvertidamente puede escribir '& null = * pointertotype;' y 'pointertotype' podría terminar siendo' NULL'. Pero a menos que use punteros, está completamente seguro. – JPvdMerwe

+0

@dribas: Técnicamente, lo anterior es __NOT__ desreferenciando a NULL. El operador Unario * (a veces referido incorrectamente como el operador de desreferencia) devuelve un valor l referente al objeto o función a la que apunta la expresión La palabra clave aquí es 'referente'. No se menciona que la dirección esté desreferenciada. Ver la Sección 5.3.1/1 de la norma. Pero estoy de acuerdo en que un programa bien formado no puede tener referencias NULL y cualquiera que haga lo anterior definitivamente debe verificar que el puntero no sea NULL antes de aplicar el operador unario *. –

1
  • ¿Cuál es la forma más típica/común de hacer esto con un objeto de C++ (que no implica la sobrecarga del operador ==)?
  • ¿Es este el enfoque correcto? es decir. ¿No debería escribir funciones que toman un objeto como argumento, sino escribir funciones de miembros? (Pero incluso si es así, por favor responda a la pregunta original.)

n, las referencias no puede ser nulo (a menos que un comportamiento indefinido ya ha sucedido, en cuyo caso todas las apuestas ya están apagados). Si debe escribir un método o no método depende de otros factores.

  • Entre una función que toma una referencia a un objeto o una función que toma un puntero al estilo de C a un objeto, hay razones para elegir uno sobre el otro?

Si tiene que representar "sin objeto", a continuación, pasar un puntero a la función, y deje que el puntero sea NULL:

int silly_sum(int const* pa=0, int const* pb=0, int const* pc=0) { 
    /* Take up to three ints and return the sum of any supplied values. 

    Pass null pointers for "not supplied". 

    This is NOT an example of good code. 
    */ 
    if (!pa && (pb || pc)) return silly_sum(pb, pc); 
    if (!pb && pc) return silly_sum(pa, pc); 
    if (pc) return silly_sum(pa, pb) + *pc; 
    if (pa && pb) return *pa + *pb; 
    if (pa) return *pa; 
    if (pb) return *pb; 
    return 0; 
} 

int main() { 
    int a = 1, b = 2, c = 3; 
    cout << silly_sum(&a, &b, &c) << '\n'; 
    cout << silly_sum(&a, &b) << '\n'; 
    cout << silly_sum(&a) << '\n'; 
    cout << silly_sum(0, &b, &c) << '\n'; 
    cout << silly_sum(&a, 0, &c) << '\n'; 
    cout << silly_sum(0, 0, &c) << '\n'; 
    return 0; 
} 

Si "sin objeto" nunca necesita ser representado , entonces las referencias funcionan bien. De hecho, las sobrecargas del operador son mucho más simples porque toman sobrecargas.

Puede usar algo como boost :: optional.

+0

También tenga en cuenta el uso de 'if (p)' y 'if (! P)' en lugar de '! = NULL' y' == NULL'. –

3

Se puede utilizar un objeto designado especial como el objeto nulo en el caso de las referencias de la siguiente manera:

class SomeClass 
{ 
    public: 

     int operator==(SomeClass &object) 
     { 
      if(this == &object) 
      { 
        return true; 
      } 

      return false; 
     } 


    static SomeClass NullObject; 
}; 

SomeClass SomeClass::NullObject; 

void print(SomeClass &val) 
{ 
    if(val == SomeClass::NullObject) 
    { 
     printf("\nNULL"); 
    } 
    else 
    { 
     printf("\nNOT NULL"); 
    } 
} 
+0

Creo que eres la única persona que realmente recibió esta pregunta ... – jww

+0

Puedes hacerlo, pero no es el enfoque más claro. ¿Por qué inventar objetos nulos si solo puedes usar punteros? Un puntero nulo significa que no apunta a ningún objeto regular. – sharptooth

Cuestiones relacionadas