Konstruktor und Virtuelle Methoden



  • Ok, habe vergessen zu erläutern, dass das Problem in der folgenden Zeile liegt.
    Es spielt im Moment keine Rolle, wie die klasse verwendet wird.

    gleichseitigesDreieck(float seite) : a(seite) {}
    

    ich möchte bloß, dass in der klasse Dreieck das Objekt mit zwei variablen instantiiert wird und in der klasse gleichseitigesDreieck nur mit einer Variable.
    Beim weglassen des Standardkonstruktors in der basisklasse bringt mir der compiler einen fehler, dass er den zugehörigen konstruktor in der Basisklasse nicht findet.
    Ich muss entweder den Standardkonstruktor oder ich einen neuen anlegen, der mir die variablen der Basisklasse benutzt:

    Dreieck(float grundseite) : g(grundseite) {}
    

    Dann habe ich das Problem, dass beim erstellen von Objekten der Klasse Dreieck so etwas zulässig ist:
    [cpp]
    Dreieck dreieck1(); //soll nicht sein
    Dreieck dreieck2(1); //soll auch nicht sein
    gleichseitigesDreieck dreieck3(2); //ok
    Dreieck dreieck4(2,3); //ok

    MfG
    Dima



  • Warum machst dus dann nicht einfach so wie ich dir das gezeigt habe?



  • Braunstein schrieb:

    Warum machst dus dann nicht einfach so wie ich dir das gezeigt habe?

    Sorry, habe ein wenig länger gebraucht um den Beitrag zu Verfassen. Danke für euere unterstützung.
    Bin nicht so flink mit den Fingern. 🙂

    d.h. Nur fürs verständnis. Man braucht, wenn Variablen in der unterklasse initialisiert werden, einen passenden kostruktor in der Basisklasse???

    2. Habt ihr etwas mehr info material Links,etc. für das stichwort virtuell? raff es einfach nicht. 😞



  • Das ist komisch formuliert und trifft es irgendwie immer. Natürlich braucht die Basisklasse den Konstruktor, den Du in der Unterklasse aufrufst. Der springende Punkt ist, du musst im Konstruktor jeder abgeleiteten Klasse (irgend)einen Konstruktor der Basisklasse explizit aufrufen, wenn die Basisklasse keinen Default-Konstruktor (den ohne Parameter) hat.





  • LordJaxom schrieb:

    Das ist komisch formuliert und trifft es irgendwie immer. Natürlich braucht die Basisklasse den Konstruktor, den Du in der Unterklasse aufrufst.Der springende Punkt ist, du musst im Konstruktor jeder abgeleiteten Klasse (irgend)einen Konstruktor der Basisklasse explizit aufrufen, wenn die Basisklasse keinen Default-Konstruktor (den ohne Parameter) hat.

    Jetzt fehlt mir zum verständnis das warum?. Wieso muss ich den explizit einen konstruktor in der Basisklasse aufrufen? Ich habe ja einen Konstruktor in der abgeleiteten Klasse erstellt und möchte nur die Variablen der abgeleiteten klasse initialisieren. Um die Vererbten Variablen zu initialisieren würde es mir einleuchten, dass man den konstruktor der Basisklasse benötigt, aber warum auch für die Variableninitialisierung nur in der abgeleiteten klasse? 😕

    Braunstein schrieb:

    zu virtual
    http://tutorial.schornboeck.net/virtual.htm
    http://www.ica1.uni-stuttgart.de/Courses_and_Lectures/C++/script/node18.html#SECTION00422000000000000000

    Danke für die links werde sie mir gleich duch den Schädel ziehen. 😉



  • Dima schrieb:

    Jetzt fehlt mir zum verständnis das warum?. Wieso muss ich den explizit einen konstruktor in der Basisklasse aufrufen? Ich habe ja einen Konstruktor in der abgeleiteten Klasse erstellt und möchte nur die Variablen der abgeleiteten klasse initialisieren. Um die Vererbten Variablen zu initialisieren würde es mir einleuchten, dass man den konstruktor der Basisklasse benötigt, aber warum auch für die Variableninitialisierung nur in der abgeleiteten klasse? 😕

    Die Basisklasse ist doch immer Teil der abgeleiteten, so musst Du den Basisklassenanteil auch immer konstruieren (lassen). Und das macht nunmal der Konstruktor der Basisklasse. Du kommst in der abgeleiteten ja auch garnicht an die private-Member der Basisklasse ran.



  • LordJaxom schrieb:

    Die Basisklasse ist doch immer Teil der abgeleiteten, so musst Du den Basisklassenanteil auch immer konstruieren (lassen). Und das macht nunmal der Konstruktor der Basisklasse.

    Das ergibt Sinn. Danke.

    LordJaxom schrieb:

    Du kommst in der abgeleiteten ja auch garnicht an die private-Member der Basisklasse ran.

    Ja schon, aber an die protected.

    MfG
    Dima


  • Mod

    Dima schrieb:

    Jetzt fehlt mir zum verständnis das warum?. Wieso muss ich den explizit einen konstruktor in der Basisklasse aufrufen? Ich habe ja einen Konstruktor in der abgeleiteten Klasse erstellt und möchte nur die Variablen der abgeleiteten klasse initialisieren. Um die Vererbten Variablen zu initialisieren würde es mir einleuchten, dass man den konstruktor der Basisklasse benötigt, aber warum auch für die Variableninitialisierung nur in der abgeleiteten klasse?

    Weil ein Konstruktor immer alle nichtstatischen Member (und dazu zählen mögliche Basisklasse) initialisiert, wenn diese keine PODs sind. PODs werden nur initialisiert, wenn sie explizit in der Initialisierungsliste auftauchen.

    Deine Verwirrung ist nicht ganz unberechtigt, allerdings liegt das Problem an anderer Stelle. Ein (Objekt des Typs) gleichseitigesDreieck ist kein Dreieck. Generell (99:1) ist es ein Fehler, von konkreten Klassen (solche, die selbst vollständige Objekte erstellen) öffentlich abzuleiten.



  • asc schrieb:

    gleichseitigesDreieck(float seite)
    : Dreieck(xxx, yyy), // <-- xxx,yyy entsprechend austauschen
      a(seite)
    {}
    

    Das sieht irgenwie getrickst aus. Gibt es keine andere möglichkeit?
    Den das Dreieck(x, y) benötig man ja gar net. Denn die Variablen von Dreieck will ich nicht benutzen.



  • Da sind sie deswegen trotzdem. Genau sowas verrät dir meist einen Designfehler.
    Du könntest gleichseitigesDreieck ja auch von Figur ableiten. Dann hast du das Problem nicht.


  • Mod

    Gegenwärtig hast du:

    Figur
               |
            Dreieck
               |
    gleichseitigesDreieck
    

    besser ist:

    Figur
               |
         AbstractDreieck
          /         \
    Dreieck      gleichseitigesDreieck
    

    AbstractDreieck stellt dabei nur das gemeinsame Interface dar.



  • camper schrieb:

    Weil ein Konstruktor immer alle nichtstatischen Member (und dazu zählen mögliche Basisklasse) initialisiert, wenn diese keine PODs sind. PODs werden nur initialisiert, wenn sie explizit in der Initialisierungsliste auftauchen.

    Deine Verwirrung ist nicht ganz unberechtigt, allerdings liegt das Problem an anderer Stelle. Ein (Objekt des Typs) gleichseitigesDreieck ist kein Dreieck. Generell (99:1) ist es ein Fehler, von konkreten Klassen (solche, die selbst vollständige Objekte erstellen) öffentlich abzuleiten.

    Ja ein design fehler ist es alle mal, aber die Aufgabenstellung verlangt es nunmal das man von einem Rechteck ein Quadrat ableiten soll. Oder von Dreieck einen Gleichseitigen und Gleichschenkligen ableiten müsse. Deswegen kommen mir auch solche Fragen hoch. Aber immer positiv sehen aus fehlern anderer lern man auch. 😃



  • camper schrieb:

    Weil ein Konstruktor immer alle nichtstatischen Member (und dazu zählen mögliche Basisklasse) initialisiert, wenn diese keine PODs sind. PODs werden nur initialisiert, wenn sie explizit in der Initialisierungsliste auftauchen.

    Sorry POD ???



  • camper schrieb:

    ...PODs werden nur initialisiert, wenn sie explizit in der Initialisierungsliste auftauchen....

    😮 😕
    Echt ?

    struct A {
       int a, b;
       A() : a(1), b(2) {}
    };
    
    struct B : A {
    };
    
    int main() {
       B b;
    

    Wird hier nicht A::A() aufgerufen ?
    Oder ist A kein POD ?

    Gruß,

    Simon2.



  • Du wartest jetzt zwar sicher auf camper, 🙂 aber:
    Eine struct mit Konstruktor ist kein POD mehr
    @Dima
    POD (Plain old data).



  • Dima schrieb:

    Das sieht irgenwie getrickst aus. Gibt es keine andere möglichkeit?
    Den das Dreieck(x, y) benötig man ja gar net. Denn die Variablen von Dreieck will ich nicht benutzen.

    Du hast hierbei ein Deckproblem, hierzu rollen wir mal alles auf (Nur bezogen auf den Regelfall, nicht auf solche Spezialitäten wir private Ableitungen...):

    Ja, umgangssprachlich kann man sagen das ein gleichseitigesDreieck ein Dreieck ist. Vererbung setzt man ein um Anzuzeigen, das es sich um ein spezialisiertere Form handelt, nicht aber für Einschränkungen. Alles was die Basisklasse als Schnittstelle unterstützt muss auch die abgeleitete Klasse unterstüzen. Die abgeleitete Klasse "ist ein" auch ein Element der Basisklasse.

    Einschränkungen stellt man besser durch Eigenschaften dar, als durch Ableitungen. Ich schätze mal du wirst es an einen extremen Beispiel verstehen:

    class Dreieck;
    class BlauesDreieck : public Dreieck;
    

    Ich hoffe mal dir leuchtet ein, das sowas nicht sinnvoll ist.

    So, da eine Abgeleitete Klasse nun alles was die Basisklassen repräsentieren auch darstellt, muss es auch alles initialisieren was dazu gehört. Dazu müssen passende Konstruktoren der Basisklasse aufgerufen werden. Falls es kein Standardkonstruktor gibt, musst du einen spezialisierten in der Initialisieungsliste aufrufen.

    Was aber grundsätzlich geht: Du kannst einen Konstruktor auch protected deklarieren, so das er von Außerhalb nicht zugreifbar ist. Aber auch das verbessert kein schlechtes Design.

    Und zu guter letzt: Vererbung lässt sich auch durch Komposition ersetzen. Bei Komposition enthält deine Klasse eine andere, gibt aber nur seine Schnittstelle frei. Du könntest z.B. sagen du schreibst eine Klasse gleichseitigesDreieck, da aber die Regeln für ein Dreieck nicht vollständig zutreffen leitest du nicht ab, sondern schreibst eine passende Schnittstelle. Bei der Implementierung kannst du aber auf das Dreieck zurückgreifen, und nimmst daher ein Dreieck als Member.

    Noch immer kein wirklich gutes Design, aber besser als die Vererbung.

    cu André



  • Dima schrieb:

    Ja ein design fehler ist es alle mal, aber die Aufgabenstellung verlangt es nunmal das man von einem Rechteck ein Quadrat ableiten soll. Oder von Dreieck einen Gleichseitigen und Gleichschenkligen ableiten müsse. Deswegen kommen mir auch solche Fragen hoch. Aber immer positiv sehen aus fehlern anderer lern man auch. 😃

    Vermutlich kommt die "Auflösung" nach der Aufgabenstellung, mit einer Erklärung warum es schlechtes Design ist ;p



  • asc schrieb:

    Ableitungen. Ich schätze mal du wirst es an einen extremen Beispiel verstehen:

    class Dreieck;
    class BlauesDreieck : public Dreieck;
    

    Ich hoffe mal dir leuchtet ein, das sowas nicht sinnvoll ist.

    Nee, das leutet mir jetzt nicht ein.
    Ein BlauesDreieck kann ja im prinzip alles von der Basisklasse erben immerhin ist es ja ein dreieck, bloß das es blau ist. Also eine Eigenschaft z.B. Farbe mehr hat. also könnte man doch die Eigenschaften und Methoden von dem Dreieck erben? Oder nicht?

    Und zu guter letzt: Vererbung lässt sich auch durch Komposition ersetzen. Bei Komposition enthält deine Klasse eine andere, gibt aber nur seine Schnittstelle frei. Du könntest z.B. sagen du schreibst eine Klasse gleichseitigesDreieck, da aber die Regeln für ein Dreieck nicht vollständig zutreffen leitest du nicht ab, sondern schreibst eine passende Schnittstelle. Bei der Implementierung kannst du aber auf das Dreieck zurückgreifen, und nimmst daher ein Dreieck als Member.

    Noch immer kein wirklich gutes Design, aber besser als die Vererbung.
    [/quote]
    Wie sieht den so eine komposition aus?
    evtl. C++ Code? Bin eher praktisch orientiert 😉



  • Dima schrieb:

    Nee, das leutet mir jetzt nicht ein.

    Okay, einleuchten wird es dir spätestens wenn du dies für alle Webfarben einmal machen darfst ;)... Es gibt Dinge die drückt man durch Eigenschaften, nicht vererbung aus. Und ob ein Dreieck nun gleichseitig ist oder nicht, ist eher eine Eigenschaft als eine eigenständige Einheit.

    Dima schrieb:

    Wie sieht den so eine komposition aus?
    evtl. C++ Code? Bin eher praktisch orientiert 😉

    class gleichseitigesDreieck
    {
      private:
        Dreieck dreieck;
        //...
      public:
      //...
    };
    

    cu André


Anmelden zum Antworten