2009-03-19 12 views
10

Ich stolperte über dieses Stück Code, der für mich völlig kaputt scheint, aber es passiert, dass thisnull ist. Ich verstehe einfach nicht, wie diese null sein kannif (! This) {return false; }

es in einem normalen Methodenaufruf ist wie

myObject->func(); 

innen MyObject::func() wir

if (!this) { return false; } 

haben, ist es eine Möglichkeit, ich kann die erste Zeile, um eine NullPointerException zu werfen, anstatt in die null (?) Methode gehen?

+0

Ist diese .net spezifisch? - Vielleicht möchten Sie das Tag ändern, viele C++ - Benutzer werden herausfiltern. Net –

+1

NullPointerException macht mich glauben, dass dies überhaupt keine C++ Frage ist. C#? – Tom

Antwort

22

Wenn Sie:

MyObject *o = NULL; 

o->func(); 

Was als nächstes passiert hängt davon ab, ob func virtuell ist. Wenn dies der Fall ist, stürzt es ab, weil ein Objekt benötigt wird, um die vtable abzurufen. Wenn es jedoch nicht virtuell ist, wird der Aufruf mit dem auf NULL gesetzten this-Zeiger fortgesetzt.

Ich glaube, der Standard sagt, dies ist "undefined Verhalten", so kann alles passieren, aber typische Compiler erzeugen nur den Code, um nicht zu überprüfen, ob der Zeiger NULL ist. Einige bekannte Bibliotheken beruhen auf dem von mir beschriebenen Verhalten: MFC hat eine Funktion namens SafeGetHandle, die für einen Nullzeiger aufgerufen werden kann und in diesem Fall NULL zurückgibt.

Sie könnten eine wiederverwendbare Hilfsfunktion schreiben wollen:

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

Sie können dann zu Beginn einer Funktion verwenden, dass alle ihre Argumente zu überprüfen, einschließlich this:

CheckNotNull(this); 
+0

Nur um Dinge zu verdeutlichen ... Dereferenzierung von NULL ist undefiniertes Verhalten des Standards. MFC sollte wirklich nicht davon abhängig sein. Wäre die Implementierung definiert, wären die Dinge anders. Wahrscheinlichkeiten sind, dass dies ein schlechter Versuch ist, eine Wettlaufbedingung zu "reparieren". –

+0

Ich denke, diese Geschichte ist angemessen: http://www.gotw.ca/conv/002.htm –

+0

Alternative Moral zur Geschichte: implementieren CheckNotNull und machen es eine Ausnahme mit der Nachricht werfen "Sei kein Arschloch, Bob ! " –

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

Warum würden Sie einen Zeiger auf die Ausnahme werfen? –

+0

Und warum testen Sie das zweimal für NULL? – Eclipse

+0

Ihr Code sieht wie C# aus, oder? – mmmmmmmm

-1

dies = = null sollte nur dann auftreten, wenn Sie eine Methode für ein gelöschtes Objekt aufrufen oder wenn etwas in den Speicher schreibt, was nicht der Fall sein sollte (und dabei den Zeiger dieses Objekts überschreiben soll).

Sie sollten untersuchen, was wirklich falsch mit Ihrem Code ist, anstatt zu versuchen, so zu umgehen.

+0

Das Löschen eines Objekts setzt seinen Zeiger nicht auf null. –

+0

+1 für Mark Ransoms Kommentar. – Tom

0

dieser Zeiger kann in solchen Fällen null geworden:

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(); 
} 

Ich denke, jemand einen schnellen Hack auf die Zugriffsverletzung Ausnahme zu vermeiden war.

+0

Ich bin nur überrascht, dass das nicht abstürzt, sobald es versucht, f anzurufen ... ich meine, wenn p NULL ist, p-> alles sollte seg-fault ... –

1

Es ist möglich, dass this null ist. Ich vermute, dass dieser Code versucht, eine Race Condition, bei der das Objekt noch nicht fertig initialisiert oder gelöscht wurde, (schlecht) zu erkennen.

+0

können Sie nicht "this" ändern . es ist ein rvalue. (= "Dies" ist kein Objekt). du kannst dir das so vorstellen, stell dir vor, "selbst" ist der this-Zeiger: T * self_ = this; #define self (self_ + 0) // Du konntest nicht tun (self + 0) = ... –

+0

Ugh, du hast Recht.Ich wollte sagen "aber es kann nicht geschrieben werden". –

1

(dies == NULL) ist undefiniertes Verhalten gemäß dem Standard. Ich denke, Sie sollten dieses Kontroll entfernen :)

Angenommen wir folgenden Anruf:

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

Das Verhalten ist schon nicht definiert, also warum die Prüfung für diese == NULL plagen tut in CSomeClass :: Methode?

EDITED: Ich gehe davon aus, dass Ihr Compiler (0 == this) behandelt, wenn Sie nicht Mitglied Variablen verwenden, aber wo würde es die virtuellen Tabellenzeiger finden? In diesem Fall kann Ihre Klasse keinen Polymoprismus verwenden.

+0

Ein Grund für das Einchecken besteht darin, den Standard ordnungsgemäß durchzusetzen, wenn Ihr Compiler dies nicht für Sie tut. –

+0

Ich würde es nicht NULL an erster Stelle nennen, dann wäre die Überprüfung überhaupt nicht erforderlich. oder - wenn ich es überprüfe - dann zur rufzeit. Warum sollte sich der Anrufer Sorgen machen, wenn der Anrufer solche unmöglichen Dinge tut? ein guter intelligenter Zeiger, der eine Behauptung für diese Situation enthält, ist in Ordnung, ich denke, –

+0

ja, behaupten (obj) im Code (nicht in der Methode) oder im intelligenten Zeiger helfen uns, Problem zu ermitteln. im Allgemeinen sah ich viele Entwickler, die nicht, was Sache sollte nie passieren und sollte geltend gemacht werden, und was sollte überprüft werden. – bayda

2

Eine Möglichkeit, diese Art von Fehlern (von Entwurf) zu fangen, verwendet eine Zeiger-Wrapper-Klasse (ähnlich einem shared_ptr), die bei der Erstellung mit einem Nullzeigerargument ausgelöst wird. Es kann auch werfen, wenn dereferenziert, aber das ist ein bisschen spät - besser als nichts, denke ich.

+0

Ich dachte zuerst boost :: shared_ptr würde werfen. aber nachdem ich nachgeschaut habe, musste ich meine antwort löschen, weil es nicht geht :(anscheinend liegt es an der implementation, ob etwas geworfen wird oder nicht. aber trotzdem, in meinem GCC bekomme ich einen assertion failure - besser als nichts :) –

Verwandte Themen