dynamic_cast und virtual functions



  • Bei Implementierung einiger Interfaces für Widgets mittels Qt entsteht eine
    Hierarchie mit 2 Basisklassen (I und QWidget):

    class I { 
      virtual void method() = 0; 
    }
    class IXyz : public I  { 
      virtual void method() { ... }; 
    }
    class IAbc : public I  { 
      virtual void method() { ... }; 
    }
    class QIXyz : public QPushButton, public IXyz 
    class QIAbc : public QFrame, public IAbc
    

    Bei flacher Hierarchie ist der Zugriff auf Interfacemethoden noch recht simpel:

    for (QWidget *w = subwidgets.first(); w; w = subwidgets.next()) {
      if (I *i = dynamic_cast<I*>(w)) {
         i->method();
      }
    }
    

    Da method() in I rein virtuell ist, funktioniert das dynamische Casting mit IXyz- und IAbc-Objekten.

    Problematisch wird es hier:

    class IXyzA : public IXyz {
      virtual void method() { ... }; 
    }
    class QIXyzA : public Q..., IXyzA
    

    (oder bei dem Versuch method() in QIXyz zu überschreiben)

    In der oben zu sehenden for-Schleife wird die Methode IXyzA::method() nie erreicht, sondern immer nur IXyz::method()

    Einzig machbare Lösungen scheinen mir bislang

    • überall bis auf die unterste Hierarchieebene rein virtuelle Funktionen zu verwenden, oder
    • zu ewig langem, schlecht wartbarem Code überzugehen, nach Art von:
    for (QWidget *w = list.first(); w; w = list.next()) {
      if (I *i = dynamic_cast<IXyzA1*>(w)) {
         i->method();
      }
      else if (I *i = dynamic_cast<IXyzA2*>(w)) {
         i->method();
      }
      else if (I *i = dynamic_cast<I*>(w)) {
         i->method();
      }
      ....
    }
    

    oder

    for (QWidget *w = list.first(); w; w = list.next()) {
      if (w->isA("Q...")) {
         ((I...*)w)->method();
      }
      else if (w->isA("Q...b")) {
         ((I...b*)w)->method();
      }
      ....
    }
    

    Das Hauptproblem ist, daß es eine Menge von I abgeleiteter Widgets gibt und auch
    jede zukünftige Spezialisierung (wie z.B. von IXyz nach IXyzA) an anderer, allgemeinerer Stelle in if..else Blöcke aufgenommen werden müsste.

    Irgendein Tipp für einen besseren Ansatz?

    Vielen Dank!



  • Hi,

    ich hab nicht verstanden wo dein Problem ist und hab versucht zu verstehen wo dein Problem liegt. Ich hab da jetzt nix gefunden. Das von mir erwartete Ergebnis habe ich auch bekommen, daher bin ich etwas verwundert. Und warum du die ganzen Typen durch testen willst weiß ich auch nicht. Hier ist das von mir getestete Beispiel indem QBase QWidget darstellen soll und Interface I:

    #include <iostream>
    struct QBase
    {
      virtual ~QBase(){}
    };
    
    struct Interface  
    {
      virtual ~Interface(){}  
      virtual void method() = 0;
    };
    
    struct QX : QBase
    {
    };
    
    struct ITest : Interface
    {
      virtual void method(){ std::cout << " ITest::Interface " << std::endl; }
    };
    
    struct IQXTest : ITest,QBase
    {
      virtual ~IQXTest(){};
      virtual void method(){ std::cout << " IQXTest : ITest,QBase " << std::endl; }
    };
    
    struct TestClass : IQXTest
    {
      virtual ~TestClass(){};
      virtual void method(){ std::cout <<  " TestClass : IQXTest " << std::endl; }
    };
    
    int main()
    {
       QBase     qb;
       IQXTest   iqxt;
       TestClass tc;
       QX        qx;
       QBase *arr[4] = {&qb,&iqxt,&qx,&tc};
    
       for(QBase ** iter = arr; iter != arr+4; ++iter)
       {
          if(Interface * iface = dynamic_cast<Interface*>(*iter))
          {
             iface->method();
          }
       }
    }
    
    /* 
     Expected Output:
    
     IQXTest : ITest,QBase
     TestClass : IQXTest
    
    */
    

    Da dieses Problem aber ansich nichts mit diesem Forum zu tun hat, sondern ein reines C++ Problem ist verschiebe ich es mal in das entsprechende Forum

    BR



  • Dieser Thread wurde von Moderator/in evilissimo aus dem Forum Andere GUIs - Qt, GTK+, wxWidgets in das Forum C++ verschoben.

    Im Zweifelsfall bitte auch folgende Hinweise beachten:
    C/C++ Forum :: FAQ - Sonstiges :: Wohin mit meiner Frage?

    Dieses Posting wurde automatisch erzeugt.



  • ich hab nicht verstanden wo dein Problem ist und hab versucht zu verstehen wo dein Problem liegt.

    Upps! Geht mir wohl auch so. Kann bislang nur sagen, dass dein Beispielcode hier das erwartete/erwünsche Ergebnis liefert.
    Habe selbst durch Variation eines größeren Programms getestet - Ein dummer Fehler - und dort leider anderes Verhalten bekommen.
    Falls es interessant scheint melde ich mich, sobald die Fehlerursache entdeckt ist..



  • Ich vermute du hast ein Mehrfachvererbungsproblem (untere Klasse von zwei Klassen abgeleitet die beide von I abgeleitet sind). In diesem Fall kann es zu solchen effekten kommen.

    Leite von I immer virtuell ab dann ist sichergestellt, dass du die Schnittstelle nicht mehrfach erbst.



  • Was mich gestern zur Fehlinterpretation verführte war
    1. der Debugger (gdb gui von kdevelop), der die falsche Quellcodezeile/Methode angesprungen hat und
    2. daß das durch die Methode veränderte QWidget nicht aktualisiert wird.
    (Durch das Beispielprogramm von evilissimo läßt sich gdb nicht verwirren.)



  • Hi evilissimo,
    vielen Dank für die Mühe übrigens ..

    Gibt es einen besonderen Grund weshalb du struct und nicht class benutzt?



  • Pdug schrieb:

    Hi evilissimo,
    vielen Dank für die Mühe übrigens ..

    Gibt es einen besonderen Grund weshalb du struct und nicht class benutzt?

    Ja weil ich schreibfaul bin 😉
    Ich spare mir public bei der Vererbung und public: für die Zugriffskontrolle weil das beides bereits default ist bei einem struct


Anmelden zum Antworten