Zeiger auf Basisklasse -> Methode einer abgeleiteten Klasse aufrufen



  • Moin moin!

    Ich versuche mal, mein Problem, so allgemein, wie möglich zu formulieren, um nicht den ganzen Code posten zu müssen.
    Aaaalso:

    Gegeben ist eine Methode

    Basisklasse* ptObjekt(string)
    

    welche aus einer map (Schlüsseltyp string) einen Pointer raussucht und zurückgibt. Die Objekte auf die die Zeiger in dieser map verweisen haben allesamt als Typ unterschiedliche, (von 'Basisklasse') abgeleitete Klassen (z.B. abgeleiteteKlasse_1, abgeleiteteKlasse_2, abgeleiteteKlasse_3).

    Nun benutze ich ptObjekt in einem bestimmten Kontext, so dass ich davon ausgehen kann, dass (wenn kein anderer Fehler auftritt...) der zurückgegebene Zeiger immer auf ein Objekt vom Typ abgeleiteteKlasse_1 zeigt.

    Ich habe also meinen Zeiger vom Typ Basisklasse* und möchte jetzt auf eine Memberfunktion (void vMachWas_inKlasse_1()) von abgeleiteteKlasse_1 zugreifen. Der Aufruf

    Basisklasse* ptMeinZeiger = ptObjekt("abc");
    ptMeinZeiger->vMachWas_inKlasse_1();
    

    ergibt einen Compilerfehler - klar, weil vMachWas_inKlasse_1() in der Basisklasse nicht definiert wurde, es ist eine Methode, die nur abgeleiteteKlasse_1 hat.

    Eine mögliche Lösung wäre es, vMachWas_inKlasse_1() in der Basisklasse rein virtuell zu deklarieren und anschließend in den abgeleiteten Klassen zu redefinieren. Dies würde aber der vorgegeben Schnittstellendefinition widersprechen, nach der vMachWas_inKlasse_1() keine eigenschaft von abgeleiteteKlasse_2 oder von abgeleiteteKlasse_3 sein soll.

    Casten des Zeigers habe ich auch schon ausprobiert, ohne Erfolg. Wobei ich mich mich damit auch nicht allzu großartig auskenne und explizites Casten sowieso eine beliebte Fehlerquelle ist.

    Wie greife ich also auf eine Methode einer abgeleiteten Klasse zu, wenn ich lediglich einen Zeiger vom Typ der Basisklasse habe?

    Vorschläge bitte hier ➡

    Gruß,

    Andre



  • Hallo,
    du solltest dir klar darüber werden, was du willst. Auf der einen Seite verwendest du eine polymorphe Menge und sagst damit, "Hallo Freunde, hier ist eine Menge von Bla-Objekten. Ich speichere alle in einer Menge, da mich der konkrete Typ nicht interessiert. Ich bin nur an der Gemeinsamkeit interessiert. Solange alle das Bla-Protokoll erfüllen ist alles dufte". Ein paar Sekunden später, sagst du dann aber "Ey man, was gebe ich auf mein dummes Geschwätz von eben. Mich interessiert sehr wohl der konkrete Typ meiner Objekte, denn ich würde gerne allen Objekten die das Blub-Protokoll beherrschen, die foobar-Nachricht senden".

    Es gibt jetzt viele Möglichkeiten. Hier ein paar:
    1. Du stehst zu deine letzten Aussage und speicherst deine Objekte in verschiedenen, passend getypten, Mengen.

    2. Du stehst zu deiner ersten Aussage und ergänzt das Bla-Protokoll entsprechend um eine foobar-Nachricht.

    3. Du bleibst unentschlossen und hammerst Zweifel mit einem dynamic_cast aus der Welt.

    if (Derived* d = dynamic_cast<Derived*>(ptMeinZeiger))
       d->fooBar();
    

    4. Du ignorierst alle einfachen Lösungen und wirfst das Visitor-Pattern auf das Problem.



  • 1. Du stehst zu deine letzten Aussage und speicherst deine Objekte in verschiedenen, passend getypten, Mengen.

    2. Du stehst zu deiner ersten Aussage und ergänzt das Bla-Protokoll entsprechend um eine foobar-Nachricht.

    Danke für die schnelle Antwort.

    Klar kann ich da jetzt wild Sachen ändern, bis es funktioniert, dafür gibts dann aber keine Punkte testiert. Mein Problem ist ja, dass ich die Schnittstelle(n) (hier z.B. den Zeiger auf die Basisklasse) vorgegeben kriege und damit die gewünschten Funktionen implementieren soll. Wenn das nicht so wäre, hätte ich erst garnich gefragt. Da es nach Deinen Angaben offensichtlich keine Alternative gibt, muss ich wohl wieder murksen. Tja, so kriegt man beigebracht guten Code in C++ zu schreiben...

    Gruß,

    Andre



  • class Base
    {
    protected:
    	std::string str;
    
    public:
    	Base(std::string str_): str(str_) {}
    	virtual ~Base() {}
    
    	void foo()
    	{
    		cout << "Base String: " << str << endl;
    	}
    };
    
    class Derived : public Base
    {
    public:
    	Derived(std::string str_): Base(str_) {}
    
    	void foo()
    	{
    		cout << "Derived String: " << str.c_str() << endl;
    	}
    };
    
    int main(void) 
    {
      Base *pBase = new Base("test");
      Derived *d = dynamic_cast<Derived*>(pBase);
      d->foo(); 
      delete pBase;
    
      cin.get();
    
      return 0;
    }
    

    warum wird beim aufruf von d->foo(); eine exception? der cast ging schief?? hm..

    cu



  • unique. schrieb:

    warum wird beim aufruf von d->foo(); eine exception? der cast ging schief?? hm..

    Vielleicht weil pBase auf kein Derived zeigt?
    [cpp]
    Base *pBase = new Derived("test");
    [/cpp]



  • warum muss das:

    Base *pBase = new Derived("test");
    

    sein?
    kann ich keinen upcast machen? dynamic_cast kann nur downcasts machen?
    cu



  • hm.. schrieb:

    warum muss das:

    Base *pBase = new Derived("test");
    

    sein?
    kann ich keinen upcast machen? dynamic_cast kann nur downcasts machen?

    Der dynamic_cast im Beispiel funktioniert auch nur weil pBase tatsächlich auf ein Derived zeigt. Du kannst keine Objekte casten, sondern nur Pointer oder Referenzen darauf.
    Mach dich einfach nochmal ein wenig über Vererbung schlau. Und vor allem achte darauf was HumeSikkins weiter oben schon geschrieben hat, dynamic_cast macht selten Sinn bzw. ist selten angebracht.



  • nagut!

    so funktionierts ja auch, dynamic_cast funktioniert also nur für downcasts? und static_cast kanns in beide richtungen...

    Base *pBase = new Base("test");
    Derived *d = static_cast<Derived*>(pBase);
    d->foo(); 
    delete pBase;
    

    cu



  • unique. schrieb:

    so funktionierts ja auch, dynamic_cast funktioniert also nur für downcasts?

    Nö. dynamic_cast kann selbstverständlich auch upcasts und crosscasts.

    unique. schrieb:

    Base *pBase = new Base("test");
    Derived *d = static_cast<Derived*>(pBase);
    d->foo(); 
    delete pBase;
    

    Der Code hat undefiniertes Verhalten. pBase dynamischer Typ ist Base, nicht Derived. Der Cast nach Derived ist damit illegal. Ein dynamic_cast würde dir dies durch einen Null-Pointer bzw. eine bad_cast-Exception (wenn du in eine Referenz castest) anzeigen.

    Btw: Bist du sicher, dass du überhaupt verstanden hast, was ein upcast/downcast ist?



  • Downcasting = wenn ein Basisklassenzeiger auf einen Zeiger einer abgeleiteten Klasse konvertiert werden soll.
    Upcasting = wenn ein Zeiger einer abgeleiteten Klasse auf einen Zeiger der Basisklasse konvertiert werden soll.

    mach ich das nicht?

    cu



  • unique. schrieb:

    Downcasting = wenn ein Basisklassenzeiger auf einen Zeiger einer abgeleiteten Klasse konvertiert werden soll.
    Upcasting = wenn ein Zeiger einer abgeleiteten Klasse auf einen Zeiger der Basisklasse konvertiert werden soll.
    mach ich das nicht?

    Du vergisst dabei eine Kleinigkeit. Ein legaler Downcast setzt voraus, dass der dynamische Typ des Objekts hinter dem Zeiger auch vom Typ der abgeleiteten Klasse (oder von einer weiter abgeleiteten Klasse) ist.

    Bei dir ist der dynamische Typ aber ein Base-Objekt und daran ändert sich durch den Cast überhaupt nichts.



  • Du vergisst dabei eine Kleinigkeit. Ein legaler Downcast setzt voraus, dass der dynamische Typ des Objekts hinter dem Zeiger auch vom Typ der abgeleiteten Klasse (oder von einer weiter abgeleiteten Klasse) ist.

    Bei dir ist der dynamische Typ aber ein Base-Objekt und daran ändert sich durch den Cast überhaupt nichts.

    Base *pBase = new Base("test");
    

    ...Pointer pBase vom typ Base zeigt auf ein Base objekt?

    wenn ich jetzt mache:

    pBase->foo();
    

    dann wird die methode der basisklasse aufgerufen...weil der Typ ja Base ist...

    Base *pBase = new Derived("test");
    

    ...der pBase Pointer vom typ Base zeigt auf ein Derived objekt?
    ...hier kann ich aber nur

    pBase->foo();
    

    aufrufen wenn ich eine public vererbung habe! ok?

    beides nennt man ja Basisklassenzeiger weil sie vom typ Base sind?

    mit dynamic_cast ändere ich nun meinen meinen Typ des Zeigers von Base auf die abgeleitete Klasse Derived...dazu muss zuvor der Basisklassenzeiger auf ein Derived Objekt zeigen, sonst gibt dynamic_cast einen NULL Pointer zurück..

    das würde dann:

    Derived *pBase = new Derived("test");
    

    entsprechen...?

    cu 😃


Anmelden zum Antworten