Basisklasse übergeben?



  • Hallo,

    ich möchte eine Basisklasse als referenz übergeben aber der compiler schreit mit Base wurde nicht deklariert:

    Base * base;
    Derived1 derived1;
    Derived2 derived2;
    MyClass myclass;
    
    if (bedingung) base = &derived1;
    else base = derived2;
    
    myclass.foo(base);
    

    und dann foo

    MyClass::foo(Base& base)
    {
           base.bar();
    }
    

    wo liegt mein fehler - oder geht das nicht?



  • gafabi schrieb:

    ich möchte eine Basisklasse als referenz übergeben aber der compiler schreit mit Base wurde nicht deklariert:

    Dem Compiler würde ich an dieser Stelle einfach glauben. Der kennt die Sprache, zumindest die syntaktischen Regeln, wahrscheinlich besser als Du.

    Dein Programmfetzen sieht gut aus. Es müsste möglich sein, wenn das "drumherum" auch stimmt. Das "drumherum" hast Du uns aber nicht gezeigt.



  • myclass.foo(*base);
    


  • Ich habe mich verkuckt. Ich will eigentlich was anderes:
    Zuerst ein Minimalbeispiel was geht:

    #include <iostream>
    
    class Mtx
    {
            public:
             void To_Pattern() {
              std::cout << "ToPattern" << std::endl;       
             };             
    };
    
    // die main
    class Base
    {
            public:
                   virtual void bar() = 0;       
    };
    class D1 : public Base
    {
            public:
                    void bar() {
                    std::cout << "D1:Base" << std::endl;
                    mtx.To_Pattern();
                    };
                    Mtx mtx;
    };
    class D2 : public Base
    {
            public:       
                    void bar() {
                        std::cout << "D2:Base" << std::endl;
                        mtx.To_Pattern();
                    };
                    Mtx mtx;
    };
    
    class MyClass
    {
            public: 
              void foo(Base* base) {
                     base->bar();
                    }
    };
    
    int main(int argc, char** argv)
    {
            Base * base = NULL;
            D1 d1;
            D2 d2;
    
            base = &d1;
    
            MyClass myclass;
            myclass.foo(base);
    
            return 0;
    }
    

    JETZT will ich aber in MyClass this (also sich selbt) verändern und möchte damit in Methode To_Pattern in Klasse Mtx ein Objekt vom Typ MyClass haebn - nur wie erreiche ich das?

    ich versuche es vergeblich so:

    #include <iostream>
    
    class Mtx
    {
            public:
                    MyClass* To_Pattern() {
              std::cout << "ToPattern" << std::endl;       
              MyClass * myclass = new MyClass();
              //mach was mit myclass und gibs zurück
              return myclass;
             };             
    };
    
    // die main
    class Base
    {
            public:
                    virtual MyClass* bar() = 0;       
    };
    class D1 : public Base
    {
            public:
                    MyClass* bar() {
                    std::cout << "D1:Base" << std::endl;
                    return mtx.To_Pattern();
                    };
                    Mtx mtx;
    };
    class D2 : public Base
    {
            public:       
                    MyClass* bar() {
                        std::cout << "D2:Base" << std::endl;
                        return mtx.To_Pattern();
                    };
                    Mtx mtx;
    };
    
    class MyClass
    {
            public: 
              void foo(Base* base) {
                     this = base->bar();
                    }
    };
    
    int main(int argc, char** argv)
    {
            Base * base = NULL;
            D1 d1;
            D2 d2;
    
            base = &d1;
    
            MyClass myclass;
            myclass.foo(base);
    
            return 0;
    }
    


  • Viel zu viel Code! Schlecht eingerückt 😞
    Trotzdem:

    this = base->bar();
    

    geht nicht, weil this ist const, man kann "this" keine andere Adresse zuweisen.
    So was macht man aber auch echt nicht!

    P.S.: Dein jetziges "Problem" deckt sich nicht mit der anfangs angedeuteten (und nicht geposteten) Fehlermeldung.



  • also jetzt platzt mir doch der kragen: jedesmal wenn ich poste heisst:

    a) Nicht immer einen neuen thread aufmachen wenn das problem rein passt. Klar ists was anderes - aber ich kann den titel nicht mehr ändern.

    b) Ich soll funktionierendes minimalbeispiel posten. Habe ich getan.

    Und jetzt störts schon wieder also bitte....

    Back to topic:

    Kann ich denn sowas machen:

    base->bar(this);
    

    und dann this verändern. geht wohl auch nicht oder?

    Falls nicht: Wie kann ich mein Problem beheben? Ich will in der Klasse ein Objekt verändern über eine Andere klasse. Unsauber?



  • gafabi schrieb:

    Kann ich denn sowas machen:

    base->bar(this);
    

    und dann this verändern. geht wohl auch nicht oder?

    this ist auch da nicht veränderbar!
    Du kannst natürlich sehrwohl non-const Methoden darauf aufrufen, aber du kannst this keine andere Adresse zuweisen! Egal wie du das drehen willst!

    Falls nicht: Wie kann ich mein Problem beheben? Ich will in der Klasse ein Objekt verändern über eine Andere klasse. Unsauber?

    Indem du einfach nicht versuchst ein Objekt such selbst ändern zu lassen. Ergo gehört das außerhalb von deiner Instanz gemacht, egal wie und egal wo.
    Bedenke, dass wenn du einem Pointer einen neuen "Wert" gibst, es also auf ein anderes Objekt zeigen lässt, das vorher referenzierte Objekt nicht mehr über diesen pointer zugänglich ist. Wenn also der Pointer der einzige ist, der auf das Objekt zeigt, musst (!) du ihn mit delete löschen, um ein Speicherleck zu verhindern.

    [OT]
    Du brauchst Mtx nicht für dein Beispiel, ebenso keine verschiedenen Ableitungen von Base für ein Minimalbeispiel. Da muss man erst ewig durch den schlecht eingerückten Texkt suchen um deine Klassen und Methoden zu finden. (Setze "Ewig" in den zeitlichen Rahmen, den man sich eines Problems annehmen "will".)
    Und das wichtigste ist einfach die Fehlermeldung, die du uns in diesem Post bisher vollkommen vorenthalten hast. Es bringt A) nichts, einen Codefetzen hinzuknallen, der vollkommen frei ohne Kontext im Raum steht, 😎 viel Code zu posten, den man erst kompilieren muss um überhaupt zu erahnen, welchen Fehler du bekommen haben könntest.
    Denn erfahrene Augen sehen anhand der Fehlermeldung meist schon welches Problem du hast. Aer eben nicht immer...



  • Ja. Du kannst this einer beliebigen Funktion übergeben und dann da mittels der Schnittstelle (öffentliche Funktionen) das Objet bearbeiten.

    Dazu muss bar lediglich einen Zeiger auf MyClass erhalten:

    class Base
    {
            public:
                   virtual void bar(MyClass* m) = 0;      
    };
    

    btw:
    Schlecht eingerückt ist es und das solltest du schon richtig machen. Die IDE deiner Wahl unterstützt dich dabei enorm.

    Und das hier:

    MyClass* To_Pattern() {
              std::cout << "ToPattern" << std::endl;      
              MyClass * myclass = new MyClass();
              //mach was mit myclass und gibs zurück
              return myclass;
             };
    

    ist böse. Du erzeugst da einen Zeiger und gibst ihn nie wieder frei. Sprich der User von To_Pattern müsste das machen, was in deinem Code aber nie geschieht. Grundsätzlich sollte immer derjenige, der Speicher explizit anfordert den auch wieder freigeben.

    @Lästermaul:

    this ist auch da nicht veränderbar!
    Du kannst natürlich sehrwohl non-const Methoden darauf aufrufen, aber du kannst this keine andere Adresse zuweisen! Egal wie du das drehen willst!

    Du scheinst da etwas zu verwechseln. this ist ein konstanter Zeiger auf ein nichtkonstantes Objekt (wenn er aus einem nichtkonstantem Objekt kommt).



  • Also danke schonmal. Leider verstehe ichs noch nicht und komme nicht auf die Lösung. Ich versuche es im Moment so:

    #include <iostream>
    
    class Pattern
    {
        public: 
             void foo(Base& base)
             {
                 base.To_Pattern(this);
             }
    };
    
    // die main
    class Base
    {
      public:
              virtual void To_Pattern(Pattern*& P) = 0;       
    
    };
    class D1 : public Base
    {
        public:
            void To_Pattern(Pattern *& P) 
            {
                std::cout << "D1:Base" << std::endl;
                //auf P arbeiten
            }
    };
    class D2 : public Base
    {
        public:       
            void To_Pattern(Pattern*& P) 
            {
                std::cout << "D2:Base" << std::endl;
                // auf P arbeiten
            }
    };
    
    int main(int argc, char** argv)
    {
            Base * base = NULL;
            D1 d1;
            D2 d2;
            base = &d1;
            MyClass myclass;
            myclass.foo(base);
            return 0;
    }
    

    Die compiler-meldung ist:

    Fehler: »Base« wurde nicht deklariert
    main.cpp: In member function »void Pattern::foo(int&)«:
    main.cpp:13: Fehler: Abfrage des Elementes »To_Pattern« in »base«, das vom Nicht-Klassentyp »int« ist
    main.cpp: In function »int main(int, char**)«:
    main.cpp:49: Fehler: »MyClass« wurde in diesem Gültigkeitsbereich nicht definiert
    main.cpp:49: Fehler: expected `;' before »myclass«
    main.cpp:50: Fehler: »myclass« wurde in diesem Gültigkeitsbereich nicht definiert
    


  • Pattern benutzt Base, also muss Base vor Pattern definiert sein.

    Vor der Base reicht eine Vorwärtsdeklaration von Pattern.
    (Siehe auch hier)



  • sorry - ich meinte diesen code:

    #include <iostream>
    
    class Pattern
    {
        public: 
             void foo(Base* base)
             {
                 base->To_Pattern(this);
             }
    };
    
    // die main
    class Base
    {
      public:
              virtual void To_Pattern(Pattern*& P) = 0;       
    
    };
    class D1 : public Base
    {
        public:
            void To_Pattern(Pattern *& P) 
            {
                std::cout << "D1:Base" << std::endl;
                //auf P arbeiten
            }
    };
    class D2 : public Base
    {
        public:       
            void To_Pattern(Pattern*& P) 
            {
                std::cout << "D2:Base" << std::endl;
                // auf P arbeiten
            }
    };
    
    int main(int argc, char** argv)
    {
            Base * base = NULL;
            D1 d1;
            D2 d2;
            base = &d1;
            Pattern pat;
            pat.foo(base);
            return 0;
    }
    

    mit folgender meldung:

    g++ -o out main.cpp
    main.cpp:7: Fehler: »Base« wurde nicht deklariert
    main.cpp: In member function »void Pattern::foo(int*)«:
    main.cpp:9: Fehler: Abfrage des Elementes »To_Pattern« in »* base«, das vom Nicht-Klassentyp »int« ist
    main.cpp: In function »int main(int, char**)«:
    main.cpp:46: Fehler: keine passende Funktion für Aufruf von »Pattern::foo(Base*&)«
    main.cpp:7: Anmerkung: Kandidaten sind: void Pattern::foo(int*)
    


  • Gleiches Problem.



  • ja ich habs gesehen. langsam kommen wir zu der meldung die mein haupt-code produziert.

    Ich bitte nochmals um folgende betrachtung:

    //Pattern.h

    #ifndef My_H
    #define My_H
    
    #include "Base.h"
    
    class Pattern
    {
            public: 
                    void foo(Base* base)
                    {
                            base->To_Pattern(this);
                    }
    };
    
    #endif
    

    //Base.h

    #ifndef Bs_H
    #define Bs_H
    
    #include "Pattern.h"
    
    // die main
    class Base
    {
            public:
                    virtual void To_Pattern(Pattern*& P) = 0;       
    
    };
    
    #endif
    

    //D1.h

    #ifndef D1_H
    #define D1_H
    
    #include "Pattern.h"
    #include "Base.h"
    
    class D1 : public Base
    {
            public:
                    void To_Pattern(Pattern *& P) 
                    {
                            std::cout << "D1:Base" << std::endl;
                //auf P arbeiten
                    }
    };
    
    #endif
    

    //D2.h

    #ifndef D2_H
    #define D2_H
    
    #include "Pattern.h"
    #include "Base.h"
    
    class D2 : public Base
    {
            public:       
                    void To_Pattern(Pattern*& P) 
                    {
                            std::cout << "D2:Base" << std::endl;
                // auf P arbeiten
                    }
    };
    
    #endif
    

    //Main.cpp

    #include "Base.h"
    #include "Pattern.h"
    #include "D1.h"
    #include "D2.h"
    
    int main(int argc, char** argv)
    {
            Base * base = NULL;
            D1 d1;
            D2 d2;
            base = &d1;
            Pattern pat;
            pat.foo(base);
            return 0;
    }
    

    Mit folgender meldung:

    Pattern.h:10: Fehler: »Base« wurde nicht deklariert
    Pattern.h: In member function »void Pattern::foo(int*)«:
    Pattern.h:12: Fehler: Abfrage des Elementes »To_Pattern« in »* base«, das vom Nicht-Klassentyp »int« ist
    main.cpp: In function »int main(int, char**)«:
    main.cpp:22: Fehler: keine passende Funktion für Aufruf von »Pattern::foo(Base*&)«
    Pattern.h:10: Anmerkung: Kandidaten sind: void Pattern::foo(int*)
    


  • Da sind wir ja schon nen Schritt weiter 🙂
    Pattern hat eine Funktion, die mit einem Base arbeitet.
    Base ist du diesem Zeitpunkt noch nicht bekannt, wird ja erst nachher deklariert.
    Mit einer Vorab-Deklaration kommst du nicht weiter, da do nur der Typenname bekannt ist, aber nicht seine Schnittstelle.

    Einzige Lösung:
    Dein "main.cpp" in verschieden Header (.h) und Implementiereungen (.cpp) aufteilen. Dann reicht im Header eine Forward-Deklaration, im .cpp wird dann der jeweils andere Header mit includiert.
    Im Prinzip sollte es reichen, Pattern von den restlichen Klassen zu separieren.

    @Drakon:
    Danke!

    Fehler: Als linker Operand einer Zuweisung wird L-Wert erfordert
    

    kommt wenn ich "this = new Test;" versuche.
    An eine Funktion das this übergeben macht aber keine Probleme! Das hatte ich so nicht erwartet.
    Aber funktionieren tut es nur, wenn das Objekt mit "new" erstellt wurde. Sonst -> SegFault!

    Interessant fand ich auch, dass das Objekt an der selben Stelle im Speicher erstellt wurde! Egal ob vorher delete oder nicht!

    #include <iostream>
    using namespace std;
    
    struct Test;
    void modify(Test*);
    
    struct Test
    {
        int num;
        void modifyThis()
        {
            num = 3;
            cout << this << endl;
            modify(this);
            cout << num << endl;
        }
        ~Test()
        {
            cout << "destroy " << this << endl;
        }
    };
    
    void modify(Test* t)
    {
        cout << "modifiy " << t << endl;
        delete t; // Auskommentieren, dann verschwindet der Segfault, die Adresse ändert sich nicht!
        t = new Test;
    }
    
    int main()
    {
        Test* t = new Test;
        t->modifyThis();
        cout << "*** " << t << endl;
        Test t3;
        t3.modifyThis(); // <== Segfault!!!
    }
    

    Also Vorsicht beim Überschreiben des this-Pointers.



  • puh...also danke schonmal...

    Einzige Lösung:
    Dein "main.cpp" in verschieden Header (.h) und Implementiereungen (.cpp) aufteilen. Dann reicht im Header eine Forward-Deklaration, im .cpp wird dann der jeweils andere Header mit includiert.
    Im Prinzip sollte es reichen, Pattern von den restlichen Klassen zu separieren.

    aj aj aj...ich überlege wie ich das ändern kann weil mir der lösungsvorschlag zu aufwändig/seltsam ist. Evtl. ist bei mir falsches design die grundlage...

    hmm...



  • //Base.h
    #ifndef Bs_H
    #define Bs_H
    
    //#include "Pattern.h" // nein!
    class Patern; // ja!
    
    // die main
    class Base
    {
            public:
                    virtual void To_Pattern(Pattern*& P) = 0;      
    
    };
    
    #endif
    

    An eine Funktion das this übergeben macht aber keine Probleme! Das hatte ich so nicht erwartet.
    Aber funktionieren tut es nur, wenn das Objekt mit "new" erstellt wurde. Sonst -> SegFault!

    Nein, nein, nein. Du verstehst das etwas völlig falsch!

    void modify(Test* t) // hier wird der this Zeiger kopiert
    {
        cout << "modifiy " << t << endl;
        delete t; // das ist ganz klar böse, wenn du ein automatisches Objekt hast
        t = new Test; // das weist t einen neuen Zeiger zu und nicht dem Objekt, auf welches t vorher gezeit hat.
    }
    

    Ich hoffe du verstehst das jetzt, ansonsten muss ich wirklich auch noch mit ASCII Art anfangen. :p



  • Hier einfach mal die Lösung zu deinem Problem.
    Es geht nicht einfacher bei solchen gegenseitigen Abhängigkeiten.

    Base.h

    #ifndef BASE_H
    #define BASE_H
    
    class Pattern;
    
    class Base 
    { 
        public: 
            virtual void To_Pattern(Pattern* P) = 0;
    };
    
    class D : public Base 
    { 
        public: 
            void To_Pattern(Pattern * P);
    };
    
    #endif
    

    Base.cpp

    #include <iostream>
    
    #include "Base.h"
    #include "Pattern.h"
    
    void D::To_Pattern(Pattern * P)
    {
        std::cout << "D1:Base" << std::endl; 
        //auf P arbeiten 
    }
    

    Pattern.h

    #ifndef PATTERN_H
    #define PATTERN_H
    
    class Base;
    
    class Pattern 
    { 
        public:
            void foo(Base* base);
    }; 
    
    #endif
    

    Pattern.cpp

    #include "Pattern.h"
    #include "Base.h"
    
    void Pattern::foo(Base* base)
    { 
        base->To_Pattern(this); 
    }
    

    main.cpp

    #include "Base.h"
    #include "Pattern.h"
    
    int main(int argc, char** argv) 
    {
        Base * base = 0; 
        D d1;
        base = &d1; 
        Pattern pat; 
        pat.foo(base); 
        return 0; 
    }
    

    Kompilieren:

    $ g++ -c Pattern.cpp
    $ g++ -c Base.cpp
    $ g++ main.cpp -o main Base.o Pattern.o
    $ ./main
    
    void modify(Test* t) // hier wird der this Zeiger kopiert
    {
        cout << "modifiy " << t << endl;
        delete t; // das ist ganz klar böse, wenn du ein automatisches Objekt hast
        t = new Test; // das weist t einen neuen Zeiger zu und nicht dem Objekt, auf welches t vorher gezeit hat.
    }
    

    In der letzten Zeile dein Kommentar:
    Ich habe auch gedacht, dass t nun auf ein neues Objekt, was ja definitiv an einer anderen Stelle im Speicher liegen müsste, zeigt.
    Die ganzen Test-Ausgaben vor, während und nach dem Modify zeigen mir in dem Fall aber ein anderes Bild:

    0x603010
    modifiy 0x603010
    destroy 0x603010
    0
    *** 0x603010
    

    Es ist IMMER die selbe Adresse, demnach wird die selbe Speicherstelle überschrieben... Oder bin ich da jetzt wieder total daneben?

    Ein späteres

    t = new Test;
    cout << t << endl;
    t = new Test;
    cout << t << endl;
    

    liefert wie erwartet

    0x603030
    0x603050
    


  • Mach mal das hier:

    void modify(Test* t) // hier wird der this Zeiger kopiert
    {
        cout << "modifiy " << t << endl;
        delete t; 
        t = new Test; 
        cout << "modifyed t: " << t << endl;
        delete t; // jetzt auch wieder aufräumen
    }
    

    Du kannst t ja gar nicht kennen ausserhalb der Funktion.. Du hast klar immer den gleichen Wert ausgegeben.

    ...
            cout << this << endl; (1)
            modify(this); (2)
        }
        ~Test()
        {
            cout << "destroy " << this << endl; (3)
        }
    ...
    
    0x603010 (1)
    modifiy 0x603010 (2) (innerhalb von modify)
    destroy 0x603010 (3)
    0 ??
    *** 0x603010 ??
    

    Mit dem obigen Code müsste zwischen 2 und 3 ein anderer Zeiger ausgegeben werden.



  • Hmmm...

    #include <iostream>
    using namespace std;
    
    struct Test;
    void modify(Test*);
    
    struct Test
    {
        void modifyThis()
        {
            cout << "=== Test::modifiyThis() (this == " << this << ") === " << endl;
            modify(this);
            cout << "after modify() " << this << endl;
            cout << "======" << endl;
        }
        ~Test()
        {
            cout << "destroy " << this << endl;
        }
    };
    
    void modify(Test* t)
    {
        cout << "modifiy " << t << endl;
        delete t;
        t = 0;
        t = new Test;
        cout << "modified t: " << t << endl;
        delete t;
    }
    
    int main()
    {
        Test* t = new Test;
        t->modifyThis();
        cout << "*** " << t << endl;
        delete t;
    }
    

    Das finale "delete t" in modify() beschert mir einen segfault beim finalen delete in main()...
    Ebenso wie ein weiterer Aufruf von modifyThis().
    Ich find das lustig, weil ich ohne weiteres noch mit dem Objekt arbeiten konnte, auf Member zugreifen usw. aber ein zweites delete geht in die Hose.

    Außerdem, egal an welcher Stelle ich noch die Adresse ausgeben lasse (in Test::modifyThis(), modify() oder main()), immer ist es die Selbe...

    Du kannst t ja gar nicht kennen ausserhalb der Funktion.. Du hast klar immer den gleichen Wert ausgegeben.

    Hmm, ich kann doch belibig Zeiger kopieren und dann vergleichen, das sollte bei egal wie gearteten Aufrufen bei gleichen Adressen das gleiche Ergebnis liefern.
    Ob ich in einer Memberfunktion jetzt "this" mit einem übergebenen Pointer vergleiche oder zwei Pointer in einer freien Funktion oder direkt, oder ...

    Ich glaub ich muss da nochmal drüber schlafen...
    Vllt. küsst mich die Erkenntnis ja noch 😕

    Grüße und Danke



  • Ich habe doch gesagt, dass das obige delete böse per se ist.

    #include <iostream>
    using namespace std;
    
    struct Test;
    void modify(Test*);
    
    struct Test
    {
        void modifyThis()
        {
            cout << "=== Test::modifiyThis() (this == " << this << ") === " << endl;
            modify(this);
            cout << "after modify() " << this << endl;
            cout << "======" << endl;
        }
        ~Test()
        {
            cout << "destroy " << this << endl;
        }
    };
    
    void modify(Test* t)
    {
        cout << "modifiy " << t << endl; // ok, gibt das gleiche, wie this
        //delete t; // böse, böse böse! 
        t = 0; // ok
        t = new Test; // ok
        cout << "modified t: " << t << endl; //ok, sollte was anderes ausgeben
        delete t; // ok
    }
    
    int main()
    {
        Test* t = new Test; // ok
        t->modifyThis(); //ok
        cout << "*** " << t << endl; //ok, gibt das gleiche, wie this
        delete t; // wenn das innere delete aktiviert ist, wird hier der speicher erneut zerstört und das gibt den fehler!
    }
    


  • Lästermaul schrieb:

    Das finale "delete t" in modify() beschert mir einen segfault beim finalen delete in main()...

    Das ist nicht verwunderlich, der Speicher wurde ja bereits freigegeben.

    Lästermaul schrieb:

    Ich find das lustig, weil ich ohne weiteres noch mit dem Objekt arbeiten konnte, auf Member zugreifen usw. aber ein zweites delete geht in die Hose.

    Ziehe keine Schlüsse aufgrund solcher Aktionen. Sobald ein Objekt zerstört wurde, resultiert der Zugriff darauf in undefiniertem Verhalten. Das bedeutet, es ist reines Glück, falls das Programm abstürzt.

    Lästermaul schrieb:

    Hmm, ich kann doch belibig Zeiger kopieren und dann vergleichen, das sollte bei egal wie gearteten Aufrufen bei gleichen Adressen das gleiche Ergebnis liefern.
    Ob ich in einer Memberfunktion jetzt "this" mit einem übergebenen Pointer vergleiche oder zwei Pointer in einer freien Funktion oder direkt, oder ...

    Du übergibst eben eine Kopie des Zeigers, deshalb führt das t = new Test; in modify() nicht dazu, dass der originale Zeiger neu zugewiesen wird. Der this -Zeiger kann nämlich nicht verändert werden.


Anmelden zum Antworten