2014-02-12 8 views
5

Ich frage mich, ob die Platzierung assert(this != nullptr); auf jeder Mitgliedsfunktion eine gute Idee ist. Ich glaube, dass der Compiler nur entscheiden kann, diese Assertion vollständig zu ignorieren, da es angenommen ist, dass this nicht null sein kann, so dass die Assert immer wahr ist und zur Kompilierungszeit aufgelöst werden kann.Nehmen Compiler an, dass "this" im Debug-Modus nicht nullptr ist?

Aber wenn der Compiler diese Annahme nicht macht, dann ist diese Behauptung sehr nützlich, um Probleme früh zu fangen.

Nehmen Compiler das an?

+0

'struct foo {int bar; Leere baz() {bar = 1; }} * Absturz = 0; crash-> baz(); 'Hey presto,' this' ist NULL. –

+4

@ JonathanPotter ist es? :) Ich sage, dass 'crash-> baz()' ein undefiniertes Verhalten ist, also kann alles danach passieren. –

+0

@ JonathanPotter: Hey presto, 'this' wurde in einen köstlichen Käseklumpen verwandelt. –

Antwort

9

Nein, Compiler nehmen das normalerweise nicht an. Es gibt sogar kommerziellen Code, der sich mit diesen Überprüfungen herumschlägt, einige behaupten nicht nur, sondern sind sogar logisch. if (!this) { doSomeWork(); }.

Obwohl Sie keine Situation erreichen konnten, in der thisNULL wäre, ohne in undefiniertes Verhalten zu laufen, wenn Sie sich der Implementierungsdetails bewusst sind, dann ist das ein Scheck, den Sie machen können; und ja, du hast Recht, es kann Hilfe beim Debuggen.

Ich würde es aber nicht überall hinstellen. Überall, in der Tat. Wenn this tatsächlich NULL ist, erhalten Sie wahrscheinlich später einen Absturz, wenn Sie auf ein Mitglied zugreifen. Wenn Sie nicht auf Mitglieder zugreifen, sollten Sie die Methode static markieren. Es bläht den Code unnötig auf.

+0

Warum würdest du es nicht überall hinstellen? – qdii

+2

Es ist Overkill - Sie werden sowieso bei einer Null-Dereferenzierung abstürzen, also fügt ein Assert-Fehler nichts wirklich Nützliches hinzu. –

+0

@qdii Ich sehe wenig Gewinn darin. –

3

Compiler akzeptieren im Allgemeinen nur etwas, wenn Optimierungen aktiviert sind. Wenn Sie also ohne Optimierungen kompilieren (assert ist aktiviert, solange Sie nicht NDEBUG definieren, was unabhängig von Optimierungen ist), funktioniert die Assert.

Es wird Probleme mit normalen Methoden zu fangen, aber denken Sie daran, dass für virtuelle Methoden der Zeigerdereferenziert wird, um sogar die Methode aufzurufen, so dass der Absturz bereits vor dieser Überprüfung aufgetreten ist. Und bei den meisten nicht-virtuellen Methoden werden die Probleme sowieso nicht unbemerkt bleiben, wenn die Methode auf Member zugreift (wenn dies nicht der Fall ist, sollte es keine Instanz-Member-Funktion sein), also ist es fraglich, ob das Hinzufügen sinnvoll ist .

+0

Nein, das ist nicht immer dereferenziert, um einen Anruf zu haben! Im Fall eines nicht virtuellen Aufrufs ist es nicht notwendig, den Zeiger zu dereferenzieren. –

+0

löschen meine Kommentare, mit Ihrer Bearbeitung macht es jetzt keinen Sinn :) – qdii

-2

Nein, es ist nur erforderlich, es auf nicht virtuellen nicht statischen Elementfunktionen zu aktivieren.

Auf statischen Elementen ist this verboten, also ok.

Auf nicht-virtuelle nicht-statische Elemente, wie Anrufe vom Compiler dann ein Aufruf wie ptr->f() statisch bestimmt werden kann wie f(ptr) etwas übersetzt werden, so dass Sie einen null this haben kann! Seltsam aber möglich. Du kannst es also behaupten.

Auf virtuelle Mitglieder, wie Anrufe dynamisch berechnet werden, kann dies nie passieren. Um die Funktion zu finden, ist es notwendig, den Zeiger zu dereferenzieren (konsultiere die vtable über den Zeiger), so dass die Maschine zu diesem Zeitpunkt abstürzt (Dereferenzierung eines Nullzeigers stürzt immer ab), kurz bevor du eine Chance hast, den effektiven Aufruf zu machen .

Experiment mit diesem:

#include <iostream> 
using namespace std; 

struct foo { 
    int bar; 
    void baz() { bar = 1; } 
    void barf() { int i = 5; cout << "barf" << this << endl;} 
    virtual void barf2() { int i = 5; cout << "barf2" <<this << endl;} 
}; 
int main() { 
     struct foo * crash = 0; 
     crash->barf(); // do not crash 
     crash->barf2(); // crash 
     //crash->baz(); // crash 
} 
+1

Warum negative Schreibweise? Sie sollten kommentieren ... –

+0

Weil "auf nicht-virtuellen nicht statischen Mitgliedern, wie Anrufe vom Compiler statisch bestimmt werden, ein Anruf wie ptr-> f() in etwas wie f (ptr) übersetzt werden kann, also können Sie habe ein null das! " Das ist falsch. Es ist kein gültiger Code, es ruft undefiniertes Verhalten auf. (Kommentare sind überflüssig, wenn die Antwort auf "Warum ist diese Antwort falsch?" In den anderen Antworten liegt.) –

2

Um zusätzlich zu dem, was bereits gesagt, macht es Sinn, nicht this für nullptr zu testen. Selbst wenn der Zeiger tatsächlich nullptr ist, wird this nicht immer Null sein.Hier ein Beispiel:

#include <iostream> 

struct A 
{ 
    void foo() { std::cout << this << std::endl; } 
    int a; 
}; 

struct B 
{ 
    void boo() { std::cout << this << std::endl; } 
    int b; 
}; 

struct C: public A, public B 
{ 
}; 

int main() 
{ 
    C *c = 0; 
    c->foo(); // this == 0 
    c->boo(); // this == 4 
    return 0; 
} 
+0

Wenn es nur * manchmal * Fehler erkennen kann, werde ich es trotzdem nehmen. – qdii