Merfachvererbung



  • @C-Sepp

    @DocShoe sagte in Merfachvererbung:

    Dreaded Diamond Problem

    Genau das.

    Abgesehen davon, das @DocShoe recht hat und man das wohl bessser nicht durch eine Vererbungsstruktur abbildet, gäbe es Virtual Inheritance, was solche Probleme löst:

    class InCall : virtual public PhoneNr
    
    class OutCall : virtual public PhoneNr
    


  • Okay...bei der Vererbungsstruktur möchte ich trotzdem bleiben. Habe virtual auch eingefügt, leider mit den selben Ergebnis?
    Abgesehen davon ist es ja vollkommen egal, ob ich in test() setphonenr mit InCall::setphonenr(str) bzw. OutCall::setphonenr(str) aufrufe?



  • @C-Sepp

    int main()
    {
        PhoneNr pnr;
        PhoneCall phc;
        phc.test("Test");   
    
        cout << pnr.getphonenr() << endl;    
    
        return 0;
    }
    

    Das passiert, wenn man nur nebenher schnell drüber schaut.
    Woher soll den pnr wissen, was du an phc übergibst?



  • Na dann viel Erfolg.



  • Ja stimmt!

    @DocShoe sagte in Merfachvererbung:

    Ein Incall/Outcall ist keine Telefonnummer. Ein Call hat mehrere (min. 2) Teilnehmer,

    Wie meintest du das? Ein Incall/Outcall ist natürlich keine Telefonnummer weil es ja zwei Klassen sind. Was ist mit Call gemeint?



  • Dein Modell sagt, dass dein InCall/OutCall Klassen Telefonnummern sind. Das ist das Gleiche als wenn ich sage ein Auto ist ein Motor. Das ist einfach falsch modelliert. Genauso könntest du eine Person so modellieren:

    class Person : public Vorname, public Nachname, public Geburtsdatum
    {
    }
    

    Es ist einfach Quatsch, und wenn du dabei bleiben möchtest: Weiterhin viel Erfolg!



  • @C-Sepp sagte in Merfachvererbung:

    Ein Incall/Outcall ist natürlich keine Telefonnummer weil es ja zwei Klassen sind.

    Eine Vererbung ist eine aber "Ist eine Beziehung". Ein BMW i3 ist ein Auto. Aber ein Anruf ist kein ausgehender und eingehender Anruf.

    Ein Telefonanruf besteht aber aus mehreren Teilnehmern, einem Typ (Telefon, Video), der Anrufdauer, usw. Jeder Telefonteilnehmer hat eine Telefonnummer, potenzielle Anrufzeiten, usw.

    Und wo immer du bestehst hörst, ist Aggregation ("besteht aus Beziehung") die erste Wahl.



  • @C-Sepp sagte in Merfachvererbung:

    pnr.getphonenr()

    Was soll hier die Funktion zurückliefern? Die Telefonnummer der eingehenden Nummer oder der ausgehenden?



  • @DocShoe sagte in Merfachvererbung:

    Es ist einfach Quatsch, und wenn du dabei bleiben möchtest: Weiterhin viel Erfolg!

    Ich stimme zu, dass das so wie es hier implemetiert wurde "Quatsch" ist und man damit sehr schnell in Teufelsküche kommt. Besonders als Anfänger.

    Allerdings sehe ich das mit der "ist-ein"-Beziehung" generell nicht ganz so dogmatisch. So ein Pattern kann manchmal sinnvoll sein und helfen, redundanten Code einzusparen. Immerhin gibt es in der OOP auch ein Konzept, das man als "Mixin" bezeichnet. Das würde ich aber nur sehr sparsam einsetzen und derart, dass es nicht zu solchen Konflikten kommt. C++ kennt eben nur Mehrfachvererbung und keine speziellen Sprachkonstrukte für Mixins, mit denen man das weniger fehleranfällig hinbekommt.

    @C-Sepp du wärst hier m.E. tatsächlich besser beraten, z.B. die Telefonnummern zu Membern von InCall/OutCall zu machen und bei diesen auch die "Ist-ein"-Beziehung zu berücksichtigen. InCall ist ein PhoneCall, also InCall : public PhoneCall ist denke ich ein besserer Ansatz für eine saubere Codestruktur die einem nicht so früh bereits um die Ohren fliegt 😉



  • Dieser Beitrag wurde gelöscht!


  • @Schlangenmensch sagte in Merfachvererbung:

    Abgesehen davon, das @DocShoe recht hat und man das wohl bessser nicht durch eine Vererbungsstruktur abbildet, gäbe es Virtual Inheritance, was solche Probleme löst:

    class InCall : virtual public PhoneNr
    
    class OutCall : virtual public PhoneNr
    

    Virtual inheritance wäre hier nun wirklich total verkehrt. Das ganze über Vererbung zu modellieren ist Quatsch, das ist klar. Aber wenn man es aus irgend einem Grund trotzdem unbedingt tun will, dann braucht man hier ganz klar zwei getrennte PhoneNr Sub-Objekte. Weil halt die eingehende Telefonnummer selten gleich der ausgehenden Telefonnummer ist.



  • @hustbaer Ja, da gebe ich dir Recht.

    Ich bin irgendwie davon ausgegangen, dass es primär darum ging sich mit Mehrfachvererbung in C++ auseinander zu setzen und das dafür ein eher unpassendes Beispiel drum herum konstruiert wurde. Daher ging es mir darum das Sprachfeature der Virtuellen Vererbung als Lösung des Dread Diamond Problems anzubringen.



  • @C-Sepp Was man machen könnte: man könnte private Vererbung einsetzen. Aber, ich glaube, ich habe das tatsächlich noch nie benutzt:

    
    #include <iostream>
    using namespace std;
    class PhoneNr
    {
    private:
      string phonenr;
    
    public:
      PhoneNr(string phnr = "0") : phonenr(phnr) {}
    
      string getphonenr() const
      {
        return phonenr;
      }
    
      void setphonenr(string phnr)
      {
        phonenr = phnr;
      }
    };
    
    class InCall : public PhoneNr
    {
      bool phcrecorded;
    
    public:
      InCall(const string& inNr) :
        PhoneNr(inNr),
        phcrecorded(false) {}
    
      bool getflag() const
      {
        return phcrecorded;
      }
    
      void setflag(bool phcrc)
      {
        phcrecorded = phcrc;
      }
    };
    
    class OutCall : public PhoneNr
    {
      bool cncestablished;
    
    public:
      OutCall(const string& outNr) :
      PhoneNr(outNr),
      cncestablished(false) {}
    
      bool getflag() const
      {
        return cncestablished;
      }
    
      void setflag(bool cncest)
      {
        cncestablished = cncest;
      }
    };
    
    class PhoneCall : InCall, OutCall
    {
    public:
      PhoneCall(const string& inNr, const string& outNr) :
        InCall(inNr),
        OutCall(outNr){}
    
      void printNrs() const
      {
        cout << "in nr: " << InCall::getphonenr() << "\n";
        cout << "out nr: " << OutCall::getphonenr() << "\n";
      }
    };
    
    int main()
    {
      const PhoneCall phc ("in number", "out number");
      phc.printNrs();
    
      return 0;
    }
    

    Abgesehen, dass die Modellierung zwischen Call und PhoneNr nicht stimmt, könnte man anstatt von privater Vererbung auch gleich Member Variablen einsetzen. Das ist halt mehr oder weniger für den Fall, dass das eine Übungsaufgabe irgendeines Lehrers ist, der auf Vererbung besteht.

    Außerdem, Parameterlose Funktionen brauchen in C++ kein void als "Parameter".

    Aber, wenn es freigestellt ist, wie du das ganze löst, würde ich über ein Redesign nachdenken. Ein Anruf hat mindestens 2 Teilnehmer (können aber auch mehr sein, oder). Jeder Teilnehmer hat eine Telefonnummer. Ein Anruf kann eventuell aufgezeichnet werden (aber, dass nur eine Seite aufgezeichnet wird, wäre zumindest mir unbekannt).



  • @hustbaer sagte in Merfachvererbung:

    ...
    Weil halt die eingehende Telefonnummer selten gleich der ausgehenden Telefonnummer ist.
    ...

    Vielleicht kann man die hier als EndPoint sehen, da sie zum Hören der InCall ist und zum Sprechen der OutCall. Allerdings werden für ein vollständiges Call-Objekt die gleichen Informationen für die Gegenstelle benötigt.



  • Vielen dank für die Ratschläge. Dann weis ich auch gleich wie die Übungsaufgabe eigentlich abgewandelt werden sollte.

    In einer weiteren Aufgabe dazu geht es darum die Funktion dial() zu schreiben:
    "Dial() erhält als Argument eine anzurufende Telefonnummer. Falls die Sitzung initialisiert wurde und die Nummer des angerufenen Telefonanschlusses "0" ist, setzt die Methode das Flag zum Verbindungsaufbau auf true"
    Die Lösung dazu sieht folgendermaßen aus:

    bool PhoneCall::dial(const string& n)
    {
        if (InCall::getphonenr()=="0" && OutCall::getphonenr() !="0")
        {
            InCall::setphonenr(n);
            OutCall::setflag(true);
            return true;
        }
        else return false;
    }
    

    Warum kann in dem Fall der Wert von phonenr über zwei Funktionen getphonenr() in der Klasse InCall und
    OutCall abgefragt werden? Sicherlich gibt es ja zwei Version von getphonenr() in beiden Klassen aufgrund
    der Vererbungshierarchie, trotzdem greifen doch beide Versionen auf das selbe Datenelement phonenr
    zu, oder wie kann man sich das im Speicher vorstellen? Danke!



  • @C-Sepp Wenn das eine Übungsaufgabe ist, zu der du eine Musterlösung hast, wäre es hilfreich, wenn du die zusammen postest. Woher sollen wir denn so wissen, wie da tatsächlich die Vererbungshierarchie aussieht und was laut Aufgabe gefordert wurde?

    Wenn das mittels privater Vererbung gelöst wurde, könnte das so funktionieren, bei virtueller Vererbung nicht.

    Wie gesagt, poste den gesamten Code, sonst ist das alles nur geraten 😉



  • @C-Sepp
    Ist das zufällig ein C++ "Buch" von Jürgen Wolf?



  • @DocShoe sagte in Merfachvererbung:

    Ist das zufällig ein C++ "Buch" von Jürgen Wolf?

    Da könnte man auch fragen "Hast du dir einen Wolf programmiert?" 😂

    SCNR



  • 😃 Ne zum Glück nicht. Moment...ich poste mal die komplette Aufgabenstellung dazu



  • Die Software einer Telekommunikationsgesellschaft soll Telefonverbindungen erfassen. Es sind die Telefonnummern von Anrufern und Empfängern sowie der Auf- und Abbau von Verbindungen zu simulieren.

    Definieren Sie zu dem Zweck eine Basisklasse PhoneNr zur Darstellung einer Telefonnummer, die nur ein Datenelement vom Typ string besitzt. Es gibt nur einen Dafault-Konstruktor, der die Telefonnummer auf "0" setzt, sowie Zugriffsmethoden, die die Telefonnummer setzen bzw. lesen.

    Die Klassen InCall und OutCall entstehen aus PhoneNr durch public Vererbung. OutCall besitzt ein zusätzliches boolsches Flag flag, das anzeigt, dass eine Telefonverbindung aufgebaut wird. InCall besitzt ein zusätzliches Flag flag, das anzeigt, dass ein Telefonanruf angenommen wurde, dass heißt dass eine Verbindung besteht. Beide Klassen besitzt einen Default-Konstruktor, der das jeweilige Flag false setzt und Zugriffsmethoden getflag() und setflag() zum Lesen bzw. Schreiben des jeweiligen Flags.

    Die Klasse PhoneCall entsteht aus InCall und OutCall durch public-Vererbung. Sie besitzt kein zusätzliches Datenelement und folgende Methoden:
    1.InitCall() setzt die als Argument übergebene Telefonnummer eines Anrufers, falls seine gespeicherte Nummer "0" ist, und initialisiert so eine Sitzung.
    2. dial().....so wie bereits beschrieben

    Die Lösung dazu unterscheidet sich nicht zu meinen Code, den ich gestern gepostet habe. Nur die Funktion dial() muss wiegesagt neugeschrieben werden.


Anmelden zum Antworten