Wieso keine Polymorphie in ctor?



  • Erst nachdem das Objekt erstellt wurde, ist die vtbl initialisiert und virtuelle Funktionen koennen benutzt werden. Ein Objekt existiert erst nach der Ausfuehrung des Konstruktors, d.h. im Konstruktor existiert es noch nicht. D.h. koennen nicht vtbl bzw. virtuelle Methoden benutzt werden. Das Verhalten ist undefiniert.



  • hustbaer schrieb:

    Gugelmoser schrieb:

    Ich sage mal, Gott sei Danke, dass es nicht funktioniert. Es ist doch einfach unlogisch, wenn ein Elternteil auf die Gene des Kindes zugreifen könnte. Das widerspricht der Natur.

    Was hat C++ mit Natur zu tun?

    Ich verbinde Objektorientierung mit der Natur. Es heißt bestimmt auch nicht zum Spaß Vererbung.



  • Gugelmoser schrieb:

    Ich sage mal, Gott sei Danke, dass es nicht funktioniert. Es ist doch einfach unlogisch, wenn ein Elternteil auf die Gene des Kindes zugreifen könnte. Das widerspricht der Natur.

    Hier greift überhaupt kein Elternteil auf das Kind zu, umgekehrt: Das Kind schiebt dem Elternteil eine geänderte Methode unter, ohne dass das Elternteil das weiß (bzw. wissen muss). Das ist allgemein üblich, siehe z.B. Template Method Pattern, es klappt halt in C++ nur im Konstruktor nicht.



  • Gugelmoser schrieb:

    hustbaer schrieb:

    Gugelmoser schrieb:

    Ich sage mal, Gott sei Danke, dass es nicht funktioniert. Es ist doch einfach unlogisch, wenn ein Elternteil auf die Gene des Kindes zugreifen könnte. Das widerspricht der Natur.

    Was hat C++ mit Natur zu tun?

    Ich verbinde Objektorientierung mit der Natur. Es heißt bestimmt auch nicht zum Spaß Vererbung.

    Vererbung ist eine is-a-Beziehung, also ein Kind ist ein Eltern ... es ist wirklich unlogisch.



  • Kinder und Eltern sind doch derselbe Typ. Aber eine Amsel "ist ein" Vogel (und ja, ich habe den Meyers gelesen...). Ein Mensch "ist ein" Säugetier. Das weißt du doch sicher selbst.



  • Also in meinem Beispiel macht das deswegen Sinn, weil die Konstruktion des jeweiligen Objektes zu einem großen Teil denselben Algorithmus verwendet. Aber in Teilen des Algorithmuses unterscheidet sich die Konstruktion basierend darauf, welchem Detailtyp die Klasse wirklich entspricht.

    Ich möchte eben Template-Method-Pattern bei der Konstruktion anwenden, gleichzeitig halte ich eine Factory oder Factory-Method aber für übertrieben. Im Speziellen geht es um eine QT-GUI-Klasse, die in den meisten Punkten gleich aufgebaut ist, aber für die Spezialfälle in den Spalten einer Tabelle unterschiedliche Elemente hat.

    Ich könnte diese natürlich auch nachträglich einfügen und im ctor der Basisklasse erstmal leer lassen... Das würde aber eine zusätzliche Iteration durch die UI-Tabelle erfordern und ein paar Zusatzinformationen muss man dann pro Zeile halt immer sammeln, daher finde ich das wiederum unschön, da man schon Doppelarbeit verursachen würde.

    Ich werde es wohl so lösen, dass ich eine initialize()-Methode anbiete, welche die Subklassen dann eben immer aufrufen müssen. Ohne Factory erscheint mir das noch am saubersten. Andere Ideen?

    Edit: Ist es auch UB, wenn ich im Der-ctor selbst eine Parent-Methode aufrufe, die wiederum polymorph agiert? Wird der vptr vor dem ctor oder danach erstellt oder ist nur definiert, dass nach dem ctor-Aufruf alles klar ist? Es klappt nämlich gerade, aber das heißt demnach nichts?



  • Gugelmoser schrieb:

    hustbaer schrieb:

    Gugelmoser schrieb:

    Ich sage mal, Gott sei Danke, dass es nicht funktioniert. Es ist doch einfach unlogisch, wenn ein Elternteil auf die Gene des Kindes zugreifen könnte. Das widerspricht der Natur.

    Was hat C++ mit Natur zu tun?

    Ich verbinde Objektorientierung mit der Natur. Es heißt bestimmt auch nicht zum Spaß Vererbung.

    Ganz schwerer Fehler, das führt bloss zu komischen Vorstellungen aus denen schlechter Code erwächst.



  • Keine Idee? Bzw. kann mir jemand das hier beantworten:

    Ist es auch UB, wenn ich im Der-ctor selbst eine Parent-Methode aufrufe, die wiederum polymorph agiert? Wird der vptr vor dem ctor oder danach erstellt oder ist nur definiert, dass nach dem ctor-Aufruf alles klar ist? Es klappt nämlich gerade, aber das heißt demnach nichts?



  • Das ist prinzipiell erlaubt, verhält sich aber komisch. Denn jede von Der abgeleitete Klasse Der2 wird sich dann wie ein Der verhalten, sprich Überschreibungen der virtuellen Funktion in Der2 werden ignoriert. Im Prinzip dasselbe Problem wie vorher, nur um eine Stufe der Vererbungshierarchie verlagert. Mit UB hat das IMHO nichts zu tun (lasse mich aber gerne eines besseren belehren, wenn jemand die Standardstelle zitiert). Schau dir mal Meyers an: http://www.artima.com/cppsource/nevercall.html

    Mir ist deine Motivation allerdings noch nicht klar. Benutzt du hier Vererbung, um Schreibarbeit zu sparen? Falls ja, dann nix gutes Idee.



  • hustbaer schrieb:

    Gugelmoser schrieb:

    hustbaer schrieb:

    Gugelmoser schrieb:

    Ich sage mal, Gott sei Danke, dass es nicht funktioniert. Es ist doch einfach unlogisch, wenn ein Elternteil auf die Gene des Kindes zugreifen könnte. Das widerspricht der Natur.

    Was hat C++ mit Natur zu tun?

    Ich verbinde Objektorientierung mit der Natur. Es heißt bestimmt auch nicht zum Spaß Vererbung.

    Ganz schwerer Fehler, das führt bloss zu komischen Vorstellungen aus denen schlechter Code erwächst.

    100% Ack @hustbaer



  • Eisflamme schrieb:

    Im Speziellen geht es um eine QT-GUI-Klasse, die in den meisten Punkten gleich aufgebaut ist, aber für die Spezialfälle in den Spalten einer Tabelle unterschiedliche Elemente hat.

    Ein GUI-Element definiert sich durch das Aussehen (Existenz und Anordnung bestimmter ELemente) und Verhalten bei Interaktion (durch User oder andere Komponenten).
    Bei dir sehen alle Widgets gleich aus und auch das Verhalten wird sich nicht unterscheiden. Einziger Unterschied: andere Daten. Daten sind eigentlich von der GUI getrennt und sollten daher zu keinen neuen Klassen führen.

    Mein Vorschlag:
    Überleg dir ein Interface, mit dem du EINE Klasse auf unterschiedliche Weise initialisieren kannst. Das macht vllt. eine weitere Klasse nötig, evtl. kann HIER mit Vererbung gearbeitet werden (sollte aber nicht zwingend nötig sein, kommt auf deinen speziellen Fall an).



  • Ne, ich spare nicht nur Schreibarbeit. Es sind auch nicht nur andere Daten drin, denn einmal habe ich ein einfaches Label + ProgressBar und das andere Mal ist darin ein Unterwidget enthalten, welches zwei Spalten enthält. Daher ist der Aufbau durchaus anders. Und ich möchte später auch einige grafische Rafinessen einbauen, die sich je Unterklasse unterscheiden.

    Bzgl. des Verhaltenscodes will ich ja nicht nur Schreibarbeit sparen, sondern eben auch doppelten Code und doppelte Ausführung von Teilen es Algos vermeiden, das wäre nämlich ebenfalls die Alternative. Wenn sich daran etwas ändert, müsste ich das sonst an mindestens zwei Stellen ändern.

    Aber auch das Verhalten der jeweiligen Klassen kann anders sein. Zurzeit ist es noch sehr ähnlich, aber ich sehe da diverse nice-to-have-Anforderungen, die ich bei ausreichend Zeit später noch in Angriff nehmen werde; und dann unterscheiden sich die Klassen ganz erheblich. 🙂

    Und was das seltsame Verhalten angeht, ist das schon in Ordnung: Wenn jemand die virtuelle Methode überschreibt und dadurch ein anderes Verhalten erzeugt, ist das durchaus sinnvoll, denke ich.

    So abstrakt ich das bisher beschrieben habe, hat jemand denn eine andere Idee im Kopf? Eigentlich finde ich das so zurzeit nicht schlecht.

    arghonaut:
    Also die Verhaltensweisen sind schon anders. Ich hatte vorher ja eine Variante, bei der ich einfach nur anders initialisiert habe. Dann hatte ich aber ganz viele if-Abfragen mit Flags drin, um diverse Schleifen (auch außerhalb des ctors) an verschiedenen Stellen dann doch anders laufen zu lassen. Die Performance spielt hier zwar keine Rolle, aber es sah nicht übersichtlich aus und durch Aufteilung in unterschiedliche Klassen finde ich es schon sauberer als mit Flags rumzuwerfen.



  • Eisflamme schrieb:

    Und was das seltsame Verhalten angeht, ist das schon in Ordnung: Wenn jemand die virtuelle Methode überschreibt und dadurch ein anderes Verhalten erzeugt, ist das durchaus sinnvoll, denke ich.

    Das funktioniert ja gerade nicht.

    Das Design würde ich nochmal überdenken. Hab aber gerade keine Zeit, sodass ich anderen mit mehr GUI-Erfahrung den Vortritt lasse 😉



  • Ach Mist, jetzt hab ich erst kapiert, was Du da sagst.

    Ja, das würde nicht funktionieren... na ja, zurzeit sehe ich keinen Fall, in dem ich zukünftig nochmals ableiten müsste, aber es ist nicht so ganz schön, ja... Solange es kein UB erzeugt, werde ich die Optimierung dieses Teils aber erstmal niedriger priorisieren... läuft ja gerade gut und ist erweiterbar genug (in den Richtungen, wie ich es vorhabe :))



  • hustbaer schrieb:

    Gugelmoser schrieb:

    hustbaer schrieb:

    Gugelmoser schrieb:

    Ich sage mal, Gott sei Danke, dass es nicht funktioniert. Es ist doch einfach unlogisch, wenn ein Elternteil auf die Gene des Kindes zugreifen könnte. Das widerspricht der Natur.

    Was hat C++ mit Natur zu tun?

    Ich verbinde Objektorientierung mit der Natur. Es heißt bestimmt auch nicht zum Spaß Vererbung.

    Ganz schwerer Fehler, das führt bloss zu komischen Vorstellungen aus denen schlechter Code erwächst.

    Das passiert halt, wenn man Gott und Familienplanung in die Programmierung bringt.



  • Vielleicht kannst du es ja so machen?

    #include <iostream>
    #include <type_traits>
    
    #define DEF_HAS_MEMBER_FUNC(member)                                                     \
      template <class T>                                                                    \
      class has_##member {                                                                  \
        private:                                                                            \
          class Fail {};                                                                       \
          template <class T2>                                                               \
          static constexpr auto check(double) -> decltype(&T2::member);                     \
          template <class T2>                                                               \
          static constexpr Fail check(int);                                                 \
        public:                                                                             \
          static const bool value = !std::is_same<decltype(check<T>(1.2) ), Fail>::value;   \
      };
    
    DEF_HAS_MEMBER_FUNC(constructorHook1)
    
    class BaseInterface { 
      public:
        virtual void doSomething() = 0;
    };
    
    template <class Child>
    class Base: public BaseInterface {
      public:
        Base() {
          static_assert(has_constructorHook1<Child>::value, "Need constructHook1 member.");
          std::cout << "-------------------------" << std::endl;
          std::cout << "Start init\n";
          std::cout << "Calling some specialized algorithm" << std::endl;
          Child::constructorHook1(this);
          std::cout << "Finish init.\n";
          std::cout << "-------------------------" << std::endl;
        }
    };
    
    class Child: public Base<Child> {
      protected:
        static void constructorHook1(Base<Child>*) { std::cout << "Constructor in child.\n"; }
      public:
        virtual void doSomething() { std::cout << "Doing something.\n"; }
        friend class Base<Child>;
        friend class has_constructorHook1<Child>;
    };
    
    int main() {
      Child c;
      BaseInterface * base_ptr = &c;
      base_ptr -> doSomething();
    }
    


  • templates + moc ist so ein Problem...
    http://developer.qt.nokia.com/doc/qt-4.8/moc.html#limitations

    Ich frag mich:
    Wie sind die Widgets angeordnet? Willst du ein Widget an einer Position durch ein anderes austauschen (immer nur eine Instanz aus dem gesamten Child-Class-komplex), oder hast du irgendwie Tabs oder eine andere Anordnung, so dass immer mehrere Instanzen unterschiedlicher Child-Klassen gleichzeitig existieren? Evtl. sogar eine statische Anordnung, die VOR dem kompilieren fest steht?



  • pyhax:
    Ich schaue mir das später nochmal an, bin gerade zu fertig, war eben laufen. Danke in jedem Fall schon Mal für die Mühe!

    arghonaut:
    Also genau, ich habe einige Tabs. Innerhalb dieser Tabs findet sich jeweils ein Kopfteil (unterscheidet sich je Tab) und eine Tabelle (unterscheidet sich hauptsächlich von der 2. Spalte vom Tab, aber es gibt auch geringere, andere Unterschiede). Neben Kopf und Tabelle unterscheiden sich die Tabs grundsätzlich noch weiter durch Buttons, die ich noch nicht eingebaut habe... Weiß auch noch nicht, wo ich die genau hinstellen möchte.

    Auch die Tabellen werden sich später vermutlich noch weiter in ihrer Struktur ändern als nur durch den Inhalt der zweiten Spalte.

    Weiters ändert sich die Anzahl der Zeilen pro Tabelle zwischendurch, weswegen ich diese hin und wieder neu erstelle. Man kann (später) dynamisch Zeilen hinzufügen oder abziehen, was die zu Grunde liegende Datenstruktur ändert. Das UI nach der Datenstruktur auszurichten, indem ich dort auch Zeilen lösche und hinzufüge, löse ich gerade dadurch, dass ich die Tabelle einfach neu erstelle. 🙄 Es macht nämlich als Benutzer keinen optischen Unterschied und war fürs Erste jetzt natürlich auch einfacher zu implementieren.

    Da geht wohl noch einiges eleganter, aber es lässt sich gerade recht einfach und zentral ändern und in die Richtung, die ich möchte, auch gut weiterentwickeln. Wenn ich kein UB erzeuge, sehe ich daher nicht viel Anlass dazu es zu ändern.

    Natürlich freue ich mich trotzdem auf Anregungen, falls man jetzt schon welche geben kann. 🙂



  • Zwei Fragen:
    * Ist für dich "Tabelle"=="QTableView"?
    * Was heißt "grafische Rafinessen", die du noch einbauen willst?

    Generell würde ich sagen:
    GUI komplett mit dem Designer gestalten. TabWidget ins Fenster ziehen, anpassen (Anzahl Tabs, Beschriftungen, usw.), dann in einem Tab das allgemeine Layout einrichten, alle Elemente markieren, kopieren, ins nächste Tab einfügen und so ändern, wie du es dort brauchst.
    Wenn Tabelle tatsächlich ein QTableView ist, kannst du die Daten eh schon ganz abstrakt per Model liefern.


Anmelden zum Antworten