Wie kann ein this pointer null sein? - beantwortet und gelöst



  • Hallo alle.

    Ich habe den interessanten Effekt, ich versuche aus einer Funktion einer Klasse I mit einem pointer auf ein II eine Memberfunktion von II auszuführen, und mein Programm crasht. Der pointer ist nicht verfügbar, nichtmal 0, sondern wenn ich den Pointer ausgeben will, crasht das programm schon. Nach bisschen Gefummel kam ich dann auf die Idee, mir man den this-Pointer des I's auszugeben, um sicherzustellen, dass es das richtige I ist, das ich aus einer weiteren Klasse heraus instanziiert habe, und das dort auch richtig initialisiert ist. Meine Verwunderung war groß, als der this-Pointer Null war. Wie kann das sein, und wie behebe ich das?

    Ich dachte zuerst, mit größter Wahrscheinlichkeit hängt es mit meiner lazy init zusammen die noch dazu auf Subklassen delegiert wird, aber das unten stehende beispiel zeigt eigentlich, dass das funktioniert. Dieser this-Pointer der null ist irritiert mich (passiert hier im Beispiel seltsamerweise nicht).

    Dieses kleine Beispiel für meine Konstruktion habe ich jetzt gerade einmal laufen lassen, und es geht natürlich (Vorführeffekt). Man stelle sich vor, dass ein A im Ablauf einer noch übergeordneten Klasse erzeugt und initialisiert wird, nicht aus der main, aber das sollte an dieser Stelle eigentlich egal sein.

    Ideen, an welcher Stelle das schief geht?

    #include <iostream>
    using namespace std;
    
    class foo{
      public:
      void doWhatAFooDoesBest(){}
    };
    
    class derivedfoo : public foo{
      //some additional functionality over a foo here
    };
    
    class A{
      public:
      A();
      virtual void initialize() = 0; //ja, ist in Wahrheit über doInit private 
      void someFunction();
    
      protected:
      foo * bar;
    
    };
    
    A::A() : bar(0){}
    
    void A::someFunction(){
        cout << "passing call on to foo (" << bar << ")" << endl;
        bar->doWhatAFooDoesBest();
    }
    
    class B : public A{
      public:
      B();
      void initialize();
      protected:
      derivedfoo * derivedbar;
    };
    B::B() : A(), derivedbar(0){}
    
    void B::initialize(){
      derivedfoo * derivedbar = new derivedfoo();
      bar = derivedbar;
      //möchte Polymorphismus der Basisklasse ausnutzen, brauche aber auch Pointer auf konkrete Subklasse
    }
    
    int main(){
       A * test = new B();
       //test->someFunction(); //schlechte Idee, da noch nicht komplett initialisiert
       test->initialize();
       test->someFunction();
       cin.get();
    }
    


  • Irgendwelche Querelen mit lokalen Variablen, die genauso heissen wie Klassenmitglieder? (In diesem Beispiel wäre derivedbar in B::initialize so ein Fall, denn das ist eine lokale Variable, die mit B::derivedbar nichts am Hut hat).

    Ansonsten ist natürlich Raterei angesagt, wenn das reduzierte Beispiel den Fehler nicht mehr zeigt. Den Zustand ansich, also dass der this-Zeiger 0 ist, kann man übrigens ganz leicht erreichen:

    A* a = 0;
    a->irgendwas();
    


  • Ja, ich hab auch schon nach versehentlich identisch benannten Variablen gesucht, kann bei größeren Hierarchien ja mal vorkommen, aber bisher nichts Verdächtiges gefunden. Wenn ich in (sagen wir) der initialize() Methode lokale Variablen hätte, die Membervariablen mit identischem Namen überdecken, könnte ich die Membervariablen doch trotzdem mittels this->memberName ansprechen, oder?

    LordJaxom schrieb:

    Den Zustand ansich, also dass der this-Zeiger 0 ist, kann man übrigens ganz leicht erreichen:

    A* a = 0;
    a->irgendwas();
    

    Würde da nicht schon der Aufruf von irgendwas() scheitern? Ich komme sozusagen bis nach irgendwas() hinein, aber wenn ich dann dort eine Membervariable benutzen möchte, crasht das programm.



  • Daniel Nichtmehrgast schrieb:

    Würde da nicht schon der Aufruf von irgendwas() scheitern? Ich komme sozusagen bis nach irgendwas() hinein, aber wenn ich dann dort eine Membervariable benutzen möchte, crasht das programm.

    Nein, es ist schlicht und ergreifend undefiniert was passiert. Und ich tippe auch auf genau so einen Fehler (Anwenden einer Funktion auf einen Null-Zeiger).



  • Hab den Fehler gefunden. Bei dieser lazy initilization muss man teuflisch aufpassen, wen man wann initialisiert. War aber wirklich genau so, ein Zeiger (im umgebenden Modul, daher läuft das Minimalmodell) wurde bei der init nicht richtig umgelegt, insofern Aufruf mit Nullpointer.



  • Genau (s. asc), laut Standard ist bereits die Dereferenzierung eines Nullpointers (und damit das Beispiel bereits vor dem Aufruf der Methode irgendwas) undefiniert. Technisch gesehen passiert aber dabei auf den mir bekannten Plattformen nichts anderes als ein Call der Methode, die als impliziten ersten Parameter einen Zeiger auf das Objekt bekommt. Dieser implizite Parameter heisst "this" und hat dann als Inhalt eben 0.


Anmelden zum Antworten