this-Pointer aus Oberklasse



  • Hey Leute,

    habe folgendes Problem und zwar geht es um folgenden Code

    class A
    {
      public:
    
        A() {x = 1;}
    
        A* foo()
        {
          return this;  
        }
    
        int x;
    
    };
    
    class B : public A
    {
      public:
    
        B() {x = 2;}
    
        int x;
    };
    
    int main()
    {
      B b;
      cout << b.foo()->x << endl; 
    
      return 0;
    }
    

    Was ich hier gerne hätte ist, dass eine 2 auf der Konsole ausgegeben wird und nicht eine 1.

    Was mir jetzt als erste Lösung einfällt ist, die Funtion foo in B einfach mit dem richtigen Rückgabetyp zu überschreiben (auch das "this" würde dann auf das richtige Objekt, nämlich das der aufrufenden Unterklasse B, zeigen)

    class A
    {
      public:
    
        A() {x = 1;}
    
        A* foo()
        {
          return this;  
        }
    
        int x;
    
    };
    
    class B : public A
    {
      public:
    
        B() {x = 2;}
    
        B* foo()
        {
          return this;  
        }
    
        int x;
    };
    
    int main()
    {
      B b;
      cout << b.foo()->x << endl; 
    
      return 0;
    }
    

    Aber vllt kennt jemand von euch noch eine elegantere Lösung. Kann man irgendwie als Rückgabetyp der Funtion foo in der Klasse A gerade den des aufrufenden Objekts wählen, d.h. wenn ein Objekt der Unterklasse B die Funktion foo ihrer Oberklasse A aufruft, soll der Rückgabewert von "foo" B* sein und "this" soll auch auf B zeigen.

    Gruß 🙂



  • Ich denke das geht nicht, aber was willst du eigentlich erreichen?



  • @MatheStein
    C++ ist statisch typisiert.



  • Mal abgesehen von der Frage, warum du nicht nur eine x-Variable nimmst: Du kannst auch einem virtuelle GetX() und SetX()-Funktion schreiben, statt foo() virtuell zu machen.



  • Man kann es auch mit CRTP lösen. Vorher sollte man aber nochmal rekapitulieren, ob es im konkreten Fall nicht bessere Lösungen gibt.



  • ipsec schrieb:

    Man kann es auch mit CRTP lösen. Vorher sollte man aber nochmal rekapitulieren, ob es im konkreten Fall nicht bessere Lösungen gibt.

    👍



  • ipsec schrieb:

    Man kann es auch mit CRTP lösen.

    Könntest du das mal kurz andeutungsweise demonstrieren? So einen Gedanken hatte ich auch, aber hab keinen Ansatz gefunden.



  • Ich werde jetzt wahrscheinlich Meilenweit vorbeischießen, aber es sieht so offensichtlich aus:

    template<typename fooT>
    class A
    {
      public:
    
        A() {x = 1;}
    
        fooT* foo()
        {
          return dynamic_cast<fooT*>(this);
        }
    
        int x;
    
    };
    
    class B : public A<B>
    {
      public:
    
        B() {x = 2;}
    
        B* foo()
        {
          return this;
        }
    
        int x;
    };
    

    Wenn das die Antwort wäre: Schade, dass static_assert/enable_if nicht gehen.



  • Bei CRTP verwendet man üblicherweise static_cast.

    Ansonsten... möglicherweise ist es das was der OP will. Es ist allerdings strenggenommen nicht das wonach er gefragt hat - schliesslich gibt es dabei keine Klasse "A" mehr, nur mehr eine Klasse "A<B>". Wenn wir dann C von B ableiten sind wir wieder wo wir angefangen haben.



  • Ja, das ist nicht ganz das, was ich mir vorgestellt hatte. Das foo in B ist wahrscheinlich nur ein Flüchtigkeitsfehler, aber dass A jetzt mit der konkreten Subklasse parametrisiert wird ... wenn dann muss foo ein Funktionstemplate sein. Und das geht halt nicht, weil man das implizite Argument nicht "zu fassen" kriegt.



  • Achja, man könnte das ganze natürlich auch gleich so machen...

    class A 
    { 
    public: 
        A() { x_ = 1; }
    
        virtual int& x() { return x_; } 
    
        int x_;
    }; 
    
    class B : public A 
    { 
    public: 
    
        B() { x_ = 2; }
    
        virtual int& x() { return x_; } 
    
        int x_;
    }; 
    
    int main() 
    { 
       B b; 
       cout << b.x() << endl; 
    
       return 0; 
    }
    


  • Man kann eine Menge machen, wenn man nicht weiß, welches Problem der OP eigentlich genau lösen will. Man kann aufgrund des abseitigen Designs auch nicht die üblichen Standardrandbedingungen annehmen. Von dem was er schreibt gehört zu den Randbedingungen aber: Die Methode darf nicht in der abgeleiteten Klasse redefiniert werden.

    Meine Überlegung war folgendermaßen: Wäre foo eine freie Funktion(stemplate), dann könnte man es so machen:

    template<class T>
    T* foo(T& t) {
      return &t;
    }
    
    B b;
    foo(b)->x
    

    Soweit, so trivial. Geht das ganze auch als Memberfunktion? Nein, obwohl bei nichtvirtuellen Memberfunktionen b.foo() im Grunde nichts anderes als foo(&b) ist, da der Typ, mit dem das Template parametrisiert ist, der Typ des impliziten this-Arguments und damit nicht (syntaktisch) zugänglich ist.

    class A { ...
    template<class T>
    T* foo(/* T* this ??? */)
    {
      return static_cast<T*>(this);
    }
    

    Irgendwie widerspricht das meinem Schönheitsempfinden 🙂


Anmelden zum Antworten