2009-03-19 37 views
10

Me tropecé con este fragmento de código que me parece totalmente roto, pero sucede que this es null. Simplemente no entiendo cómo esto puede ser nullif (! This) {return false; }

que está dentro de una llamada a un método normal, como

myObject->func(); 

dentro MyObject::func() tenemos

if (!this) { return false; } 

¿hay alguna manera de que pueda tener el primera línea para arrojar un NullPointerException en lugar de entrar en el método null (?)?

+0

¿Es esto específico para .net? - Es posible que desee cambiar la etiqueta, muchos usuarios de C++ van a filtrar .net –

+1

NullPointerException me hace creer que esto no es una cuestión de C++ en absoluto. DO#? – Tom

Respuesta

22

Si usted tiene:

MyObject *o = NULL; 

o->func(); 

Lo que sucede después depende de si es func virtual. Si es así, se bloqueará, porque necesita un objeto para obtener el vtable. Pero si no es virtual, la llamada continúa con este puntero establecido en NULL.

Creo que el estándar dice que esto es un "comportamiento indefinido", por lo que cualquier cosa podría pasar, pero los compiladores típicos simplemente generan el código para no verificar si el puntero es NULL. Algunas bibliotecas bien conocidas se basan en el comportamiento que describí: MFC tiene una función llamada algo así como SafeGetHandle que se puede invocar con un puntero nulo y devuelve NULL en ese caso.

Es posible que desee escribir una función auxiliar reutilizable:

void CheckNotNull(void *p) 
{ 
    if (p == NULL) 
     throw NullPointerException(); 
} 

entonces usted puede utilizar que, al inicio de una función para comprobar todos sus argumentos, incluyendo this:

CheckNotNull(this); 
+0

Solo para aclarar cosas ... desreferenciar NULL es un comportamiento indefinido según el estándar. MFC realmente no debería depender de esto. Si se definiera la implementación, las cosas serían diferentes. Las posibilidades son que este es un pobre intento de "arreglar" una condición de carrera. –

+0

creo que esta historia es apropiada: http://www.gotw.ca/conv/002.htm –

+0

Alternativa moral a la historia: implemente CheckNotNull y haga que arroje una excepción con el mensaje "No seas un gilipollas, Bob" ! " –

1
if(this == null) 
    throw new NullPointerException; 
if(!this) 
    return false; 
+0

¿Por qué lanzar un puntero a la excepción? –

+0

¿Y por qué probar esto dos veces para NULL? – Eclipse

+0

Tu código parece C#, ¿no es así? – mmmmmmmm

-1

este = = null solo debería ocurrir si está llamando a un método en un objeto eliminado, o si algo está escribiendo en la memoria que no debería (y sobreescribiendo el puntero del objeto en particular).

Debe investigar qué es lo que está realmente mal con su código en lugar de tratar de evitarlo de esta manera.

+0

Eliminar un objeto no establece su puntero en nulo. –

+0

+1 por el comentario de Mark Ransom. – Tom

0

este puntero puede llegar a ser nula en casos como estos:

class Test 
{ 

public: 
    bool f(); 

private: 
    int m_i; 
}; 


bool Test::f() 
{ 
    if(!this) 
    { 
     return false; 
    } 

    m_i = 0; 
    return true; 

} 


int main(int argc, char **argv) 
{ 
    Test* p = new Test; 
    delete p; 
    p = NULL; 

    p->f(); 
} 

supongo que alguien hizo un corte rápido para evitar que la excepción de acceso violación.

+0

Estoy sorprendido de que no se cuelgue tan pronto como intente llamar f ... Es decir, si p es NULL, p-> cualquier cosa debe seg-fault ... –

1

Es posible que this sea nulo. Sospecho que este código está tratando de (mal) detectar una condición de carrera, donde el objeto aún no ha terminado de ser inicializado, o ha sido eliminado.

+0

no se puede cambiar "esto" . es un valor. (= "esto" no es un objeto). puedes pensarlo de esta manera, imagina que "yo" es el puntero: T * self_ = this; #define self (self_ + 0) // no podría hacer (self + 0) = ... –

+0

Ugh, tienes razón.Quise decir "pero no se puede escribir". –

1

(this == NULL) es un comportamiento indefinido de acuerdo con la norma. Creo que se debe eliminar esta comprobación :)

Supongamos que hacemos la siguiente llamada:

((CSomeClass*) 0)->method(); 

El comportamiento es ya definido, ¿por qué preocuparse de hacer el cheque para este NULL == en CSomeClass :: método?

Editado: Asumo que su compilador se encargará (0 == esto) si no utiliza variables miembro, pero ¿dónde se encuentra el puntero de la tabla virtual? En este caso, su clase no puede usar polymoprhism.

+0

Una razón para poner el cheque es hacer cumplir el estándar correctamente, si su compilador no lo hace por usted. –

+0

No lo llamaría NULL en primer lugar, entonces el cheque no sería necesario en absoluto. o - cuando reviso - entonces a la hora de la llamada. ¿Por qué debería preocuparse el que llama cuando la persona que llama hace cosas tan imposibles? un buen puntero inteligente que contiene una afirmación para esa situación está bien. Creo que –

+0

sí, assert (obj) en el código (no en el método) o en el puntero inteligente nos ayudan a detectar el problema. en general, vi muchos desarrolladores que no pensaban qué cosas nunca deberían pasar y que deberían afirmarse y qué cosas deberían verificarse. – bayda

2

Una forma de atrapar este tipo de errores (por diseño) es utilizar una clase pointer-wrapper (que se parece a un shared_ptr) que se lanza al crear con un argumento de puntero nulo. Puede lanzar también cuando se desreferencia, pero eso es un poco tarde, mejor que nada, supongo.

+0

pensé que el primer impulso :: shared_ptr arrojaría. pero después de buscarlo, tuve que eliminar mi respuesta, porque no es nada :(aparentemente depende de la implementación si algo se arroja o no, pero sin embargo, en mi GCC recibo una falla de aserción, mejor que nada :) –

Cuestiones relacionadas