"to MI or not to MI"



  • Xin schrieb:

    volkard schrieb:

    Xin schrieb:

    volkards Posting verstehe ich nicht

    Das ist ein ideologisches Problem.
    Lies meins bitte noch mehrmals ganz unvoreingenommen. Es müßte "klick" machen. Bitte, bitte.

    Dass Programme, die nicht fertig geschrieben sind, fehlerhaft funktionieren, halte ich für ziemlich normal.

    lölchen 🤡
    Daß wergen Unfertigkeit Programme Fehler werfen, halte ich für normal. Daß sie einfach fehlerhaft laufen, halte ich für Sünde.
    HIER haben wir ein ganz entschiedenes Problem. Mir sind schon Projekte gestorben. Ich weiß, daß es darum geht, so zu programmieren, daß der Compiler Flüchtigkeitsfehler aufdeckt.

    Xin schrieb:

    Ich fasse Deine Kritik aber durchaus auf, sehe da durchaus auch einen interessanten Beitrag, der aber nichts mit Mehrfachvererbung zu tun hat. Das kann Dir auch bei Sprachen passieren, die Mehrfachvererbung gar nicht unterstützen, dafür reicht Vererbung alleine und eben zu vergessen, eine Funktion entsprechend anzupassen.

    Das ist auch mein Kritikpunkt.

    Mit

    class Address
      : protected Id
      , public    Street
      , public    HouseNumber
      , public    ZipCode
      , public    Town
    {};
    

    drückst Du aus, daß die Adresse eine Hausnummer IST (und nicht HAT). Mit allen KOnsequenzen. Alles, was eine Hausnummer KANN (Funktion), KANN auch eine Adresse tun. Das ist doch Unfug. Und tödlich.

    Java-mäßige Interfaces reinzuerben ist hingegen ok, so mehrfach Du magst. Selten schlau, aber auch selten tödlich.

    Xin schrieb:

    Ich bin als Student da vergleichsweise Amateur.
    Ich bin sicher, Du löst dieses argumentative Problem.

    Klar doch. Ganz pragmatisch: Es geht nicht anders. Worauf müßte sich zuguterletzt ein Richter im Zivilprozess verlassen, der kein eigenes Fachwissen hat? Bitte auf die Bücher und nicht auf die Wünschelrutengänger, Naturkeilkundler, Verschwörungstheoretiker und Mehrfachvererber.

    Xin schrieb:

    Ich schrieb bereits: Unser Verständnis von Programmierung hängt davon ab, was wir die letzten 60 Jahre getan haben, wenn wir programmiert haben. Für meine Sprache habe ich eine ausführliche Umfrage gemacht. Diejenigen, die am wenigsten zum Thema beizutragen hatten, waren Informatiker und erfahrene Programmierer.

    Mhhm. Vielleicht haben die erfahrenen Programmierer nicht ernst genommen?

    Xin schrieb:

    Die wissen nämlich, wie man's "richtig" macht, die machen das nämlich schon immer so und deswegen denken sie auch nichts anderes. Mathematiker, Ingenieure, Schüler haben da teilweise ganz andere Vorstellungen.

    Klar. Und junge Programmierer auch. Denen ist noch kein Projekt gestorben. :p

    Xin schrieb:

    Die sind in ihren Vorstellung nicht beschränkt. Die Informatiker fielen mit einem Satz auf "Habe ich mir nie Gedanken drüber gemacht.", denn sie haben ja gelernt, was richtig ist. Wir haben aber nie gelernt unser Verständnis in Frage zu stellen.

    Und deswegen hörst Du nicht auf gute Programmierer? Unlogisch. Was soll die Sprache werden? Eine, mit der sich Anfänger schnell anfreunden können, die aber ganz sicher nicht für nicht-winzig-kleine Programme taugt? Ähm haben wir mit Java doch schon. Gut, man müßte noch ein paar Fehler einbauen, um die Anforderung ganz zu erfüllen. MI wäre ganz toll, in der Tat.

    Xin schrieb:

    Gib mir ein Argument, das ich noch nicht kenne. Fachbücher habe ich selbst genug. Gerade im Bereich Compilerbau und Sprachdesign steht in den meisten nicht viel Interessantes drin. Wenn Du was Gutes für mein Thema weist, nur zu.

    Meyers, Effektiv C++ programmieren.

    So, und nur zum Positiven:

    struct ZipCode
      : public Integer
    {};
    

    Das ist eine gute Idee. Allerdings brauchen die eingebauten Typen auch einen gewissen Bestandsschutz. Wie das Verbot der Überladung von Operatoren für eingebaute Typen, denn sonst kommt so ein Schlawiener drauf und definiert die um und alles geht kaputt.
    Wenn ich jetzt den Wertebereich für ZipCodes einschränke auf nur 00000 bis 99999, dann können ZipCodes nicht mehr alles, was Integers können. Ich bin sicher, Dir fällt auf, wie bedenklich das ist.
    Du hast vererbt, Du hast VERSPROCHEN, daß jeder ZipCode alles kann, was ein Integer kann. Völlig unpassende Dinge wie die Rechenoperationen, falsche Dinge wie Ein-Ausgabe und gute Dinge: das Speicherlayout Konstruktorern, Destruktor und die Vergleichsoperationen. Ähm. Wir weh muss man Dir tun, bis Du anfängst, es andersrum zu machen? Zieh Dir doch nur die Sachen rein, die Du haben magst und als richtig auch absegnen kannst und nur die.



  • @Xin: Wie würde man in deiner Sprache die Klassen "Rectangle" und "Square" in einem Vektorzeichenprogramm definieren. Könnte man diese voneinander ableiten?

    MfG SideWinder



  • SideWinder schrieb:

    @Xin: Wie würde man in deiner Sprache die Klassen "Rectangle" und "Square" in einem Vektorzeichenprogramm definieren. Könnte man diese voneinander ableiten?

    Doppelspoiler. http://pastebin.de/33083



  • SideWinder schrieb:

    @Xin: Wie würde man in deiner Sprache die Klassen "Rectangle" und "Square" in einem Vektorzeichenprogramm definieren. Könnte man diese voneinander ableiten?

    Wen ableiten wovon?

    Rechteck "ist ein" Quadrat mit zusätzlicher Komponente? Quadrat "ist ein" Rechteck mit zwei Komponenten, die identisch sein müssen?

    Das hat nichts mit meiner Sprache zu tun, das ist eine sprachunabhängige Designfrage. Ich würde es wohl so modellieren, wobei das hier kein Beispiel von Codeeffizienz darstellt, sondern ein Beispiel meiner Denke:

    class X
      : public Double {}
    
    class Y
      : public Double {}
    
    class Punkt
      : public X
      , public Y {}
    
    class Greifpunkt
      : public Punkt {}
    
    class Grafikobjekt
      : public Greifpunkt {}
    
    class ViereckigesGrafikObjekt
      : public Grafikobjekt {}
    
    class Strecke
      : public Double {}
    
    class Seitenlänge
      : public Strecke {}
    
    class SeitenlängeX
      : public Strecke {}
    
    class SeitenlängeY
      : public Strecke {}
    
    class SeitenlängeXY
      : public SeitenlängeX {}
      : public SeitenlängeY {}
    
    class Radius
      : public Strecke {}
    
    class Quadrat
      : public ViereckigesGrafikObjekt
      , public Seitenlänge {}
    
    class Rechteck
      : public ViereckigesGrafikObjekt
      , public SeitenlängeXY
     {}
    
    class Kreis
      : public GrafikObjekt
      , public Radius {}
    

    Jetzt sagst Du natürlich erstmal zurecht "Was ist das für'n Scheiß, da klassifiziere ich mich ja kaputt, das geht mit Membern VIEL schnell". Das ist richtig, aber Du kannst aber viel weniger Informationen statisch im Datentyp transportieren. Ein Double ist XKoordinate, YKoordinate, Seitenlänge für X und Y oder Seitenlänge für X oder Y... Funktionen fragen immer nur nach double. Möchte ich die Kreisfläche berechnen, frage ich nach Radius, nicht nach double. Ich kann auch nicht versehentlich Quadrate reinwerfen, die ja genauso wie Kreise sind: Ein Punkt mit zusätzlichem Double.
    Hier kam das Argument auf, dass die IDE für das Schreiben von Code hilfreich ist. Leider kann die IDE aber nicht bestimmen, welche Funktion die richtige ist und manchmal findet sie zuerst die falsche, dann übergebe ich mein Double und der Bug ist fertig. Das geht hier nicht. Ein Viereck hat keinen Radius => der Compiler verweigert die Kompilierung.

    In meiner Sprache ist das bedeutend kürzer. Stell Dir das hier nicht als Ziel, sondern als Ausgangspunkt vor, an Du losgehst. Ich schrieb, dass ich in der Mehrfachvererbung Potential sehe, nicht, dass C++ es optimal nutzt. Dies zeigt das zu erreichende Ziel, wie man es in C++ formulieren muss - also leider mit viel Boilerplate: Jedes Datum hat seinen eigenen, eindeutigen Typ.

    Ich stelle meine Sprache vor, wenn ich der Meinung bin, es zeigen zu wollen. Für dieses Konzept habe ich die Syntax komplett umgebaut und vielleicht fällt mir irgendwann noch etwas Interessantes ein, was wieder Änderungen mit sich bringt.

    Ich habe noch nicht alles ausprobiert und ich habe keinen Bock mit volkard & Co rumzudiskutieren, ob meine Syntax jetzt schon perfekt ist oder nicht. Ich muss so oder so ausprobieren und in der Zwischenzeit hat volkard mir schon oft genug erklärt, dass er - freundlich formuliert - meinen Ansatz nicht schätzt.
    Also probiere ich lieber mit ausgewähltem Publikum, das spart mir Zeit. Das Posting gestern waren 2 Stunden, die ich auch für anderes gut hätte gebrauchen konnte - sich aber dank Dir wenigstens gelohnt haben, weil es eben doch immer mal wieder noch kleine "Aha"-Effekte gibt, wenn sich Leute konstruktiv mit den Dingen auseinander setzen. Bedauerlicherweise wissen die meisten aber schon, wie man es richtig macht. Nicht umsonst haben wir soviele Bundestrainer im Land 😉

    EDIT:
    Ich habe diese Klassenhierarchie jetzt mal in meiner Sprache definiert, das sind 9 (durchaus lesbare) Zeilen in der kürzesten Form, bzw. 17, wenn ich die Leerzeilen zwischen jeder einzeiligen Definition dazupacke. Es sind weniger explizite Definitionen erforderlich. 11 Zeilen, wenn ich die Mehrfachvererbungen untereinander schreibe, also 19 mit Leerzeilen.



  • Wie genau verhinderst du jetzt dass ich den Radius von einem Kreis negativ setze?



  • Shade Of Mine schrieb:

    Wie genau verhinderst du jetzt dass ich den Radius von einem Kreis negativ setze?

    <sarcasm>
    Ich glaube, Du hast da ein wesentliches K.O. Kriterium erkannt.

    Man kann auch keinen Kaffee damit kochen. Ich glaube, ich sollte die Programmierung aufgeben, wenn's nichtmals Kaffee kochen kann.
    </sarcasm>

    PS: Wenn das jetzt ernsthaft die schärfste Kritik von Dir war, die Dir für dieses Beispiel eingefallen ist, dann kann ich so falsch ja gar nicht liegen.



  • Ich fang an zu glauben, dass Xin prädikativ Typen will wie in Cecil.



  • Xin schrieb:

    PS: Wenn das jetzt ernsthaft die schärfste Kritik von Dir war, die Dir für dieses Beispiel eingefallen ist, dann kann ich so falsch ja gar nicht liegen.

    Es ist eine ernste Frage.
    Ich stelle sie mal allgemeiner: wie garantierst du Invarianten?

    Wenn Postleitzahl von Integer erbt, wie beschraenke ich Postleitzahlen auf 0 bis 99999 ? Oder woher weiss der Kreis, dass sich sein Ursprung geaendert hat um sich neu zuzeichen? Etc.



  • Zeus schrieb:

    Ich fang an zu glauben, dass Xin prädikativ Typen will wie in Cecil.

    Cecil? Da finde ich Damenmode!? ^^

    Habe noch eine Mono-Library gefunden.

    Shade Of Mine schrieb:

    Es ist eine ernste Frage.
    Ich stelle sie mal allgemeiner: wie garantierst du Invarianten?

    Wie garantiere ich Invarianten mit Membern?

    Wenn ich einem Kreis ein double als Radius mitgebe, wie garantiere ich da, dass der Kreis einen positiven Radius besitzt?

    Shade Of Mine schrieb:

    Wenn Postleitzahl von Integer erbt, wie beschraenke ich Postleitzahlen auf 0 bis 99999 ?

    In dem Fall muss ich wohl für PLZ einen Konstruktor schreiben, der das verifiziert und eine Initialisierung mit unzulässigen Werten ggfs. ablehnt, wie ich für Radius auch tun müsste, wenn ich ein Problem mit negativen Radien habe.

    Ich sehe hier keinen Unterschied zur Initialisierung von Membern in der Initialisierungsliste - und, je nach Implementierung auch nicht im Konstruktor der Address-Klasse.

    Shade Of Mine schrieb:

    Oder woher weiss der Kreis, dass sich sein Ursprung geaendert hat um sich neu zuzeichen? Etc.

    Auch hier sehe ich das Problem erstmal nicht im Aufbau der Datentypen, Du kannst genauso Events verwenden oder eben den Zugriff auf den Schreibzugriff von Punkt im Grafikobjekt erstmal abfangen. Das wäre allerdings in C++ wieder nicht formulierbar, ohne dass man überladbare Methoden hat. Ich selbst mache zwischen Getter- und Setter-Funktionen und dem direkten Zugriff auf Variablen keinen so großen Unterschied.

    Aber es gibt vermutlich noch eine weitere vorstellbare Lösung, die in C++ aber gar nicht formulierbar ist und ich bei mir noch nicht ausprobieren konnte. Soweit ist meine Sprache leider noch nicht. Vielleicht kann ich dazu Ende des Jahres was sagen.



  • Xin schrieb:

    Shade Of Mine schrieb:

    Wenn Postleitzahl von Integer erbt, wie beschraenke ich Postleitzahlen auf 0 bis 99999 ?

    In dem Fall muss ich wohl für PLZ einen Konstruktor schreiben, der das verifiziert und eine Initialisierung mit unzulässigen Werten ggfs. ablehnt, wie ich für Radius auch tun müsste, wenn ich ein Problem mit negativen Radien habe.

    Hier sehe ich zwei Probleme. Das erste ist sowas (Pseudocode):

    func(Integer& i1, Integer i2) {
        i1 -= i2;  // gültig und definiert für Integer
    }
    
    func(kreis1.radius, kreis2.radius); // huch, plötzlich Fehler, obwohl ein Radius ein Integer ist
    

    Radius ist hier Integer statt Double der Einfachheit halber, das Argument ist dasselbe.
    Das Problem hat man mit Membern nicht, da bei

    kreis1.setRadius(kreis1.getRadius() - kreis2.getRadius())
    

    eben nicht garantiert ist, dass das keinen Fehler wirft, im Gegensatz zum -= bei Integern.

    Das zweite ist, dass es bei deiner Klassenhierarchie ja eigentlich heißen müsste:

    func(kreis1, kreis2)
    

    Aber welcher Integer beim Kreis ist denn gemeint? Radius, Durchmesser, Umfang? Gut, hier könnte man es sich eventuell denken, aber was ist beim Rechteck?
    Das „is-a“, was immer so runtergebetet wird, hat ja einen Grund: A is-a B ⇒ A kann wie ein B behandelt werden. Und ich sehe hier nicht, dass ich einen Kreis oder ein Rechteck wie einen Integer behandeln kann.



  • Xin schrieb:

    In dem Fall muss ich wohl für PLZ einen Konstruktor schreiben, der das verifiziert und eine Initialisierung mit unzulässigen Werten ggfs. ablehnt, wie ich für Radius auch tun müsste, wenn ich ein Problem mit negativen Radien habe.

    Ich sehe hier keinen Unterschied zur Initialisierung von Membern in der Initialisierungsliste - und, je nach Implementierung auch nicht im Konstruktor der Address-Klasse.

    Was wenn ich jetzt aber:

    void add(Integer& lhs, Integer& rhs) {
      lhs+=rhs;
    }
    
    Postleitzahl plz(12345);
    add(plz, 700000);
    

    schreibe?

    Der Ctor von Postleitzahl validiert 12345 korrekt und es passt. Nun addiert add aber 700000 drauf und wir haben eine ungueltige Postleitzahl. Was tust du hier um das zu verhindern?

    In Postleitzahl kannst du den operator+= fuer Integer ja nicht neu definieren - es sei denn der waere virtual. Ist er das?

    Bzw: wie verhinderst du, dass ich Sachen mache die keinen Sinn ergeben: zB eine Postleitzahl mit einer Telefonnummer zumultiplizieren?

    Mich interessiert dabei die Umsetzung in C++ (wir koennen uns auch gerne auf eine andere Sprache einigen). Deine Sprache wuerde ich aus der Diskussion lieber raus lassen, da es keine Sprache ist die wir beide kennen.



  • ipsec schrieb:

    Xin schrieb:

    Shade Of Mine schrieb:

    Wenn Postleitzahl von Integer erbt, wie beschraenke ich Postleitzahlen auf 0 bis 99999 ?

    In dem Fall muss ich wohl für PLZ einen Konstruktor schreiben, der das verifiziert und eine Initialisierung mit unzulässigen Werten ggfs. ablehnt, wie ich für Radius auch tun müsste, wenn ich ein Problem mit negativen Radien habe.

    Hier sehe ich zwei Probleme. Das erste ist sowas (Pseudocode):

    func(Integer& i1, Integer i2) {
        i1 -= i2;  // gültig und definiert für Integer
    }
    
    func(kreis1.radius, kreis2.radius); // huch, plötzlich Fehler, obwohl ein Radius ein Integer ist
    

    Radius ist hier Integer statt Double der Einfachheit halber, das Argument ist dasselbe.

    Das Argument ist sehr gut und bekannt.

    Fakt ist, dass ein Klasse, die eine Einschränkung ihrer Basis hat, natürlich nicht public erben darf. Ein Kreis mit einem öffentlichen Member double Radius kann auch zerlegt werden und zwar genauso, wie Du es beschrieben hast. Es gilt also Inhalt von Integer zu schützen.

    Eine Klasse Radius darf diese Operationen also nicht öffentlich erben, wenn sie Einschränkungen macht. Im Prinzip ist das eine protected Ableitung mit einem operator Integer & const(). Radius darf Integer modifizieren, externe haben kein Zugriffsrecht. Du kannst zum Beispiel einen (Integer const &) zurückgeben, aber den bekommst Du nicht mit dem -= Operator verbogen. Also entweder beschreibst Du in Radius, was Du mit dem Radius machen darfst oder Du kannst ihn nicht in eine Funktion packen,

    ipsec schrieb:

    Das zweite ist, dass es bei deiner Klassenhierarchie ja eigentlich heißen müsste:

    func(kreis1, kreis2)
    

    Aber welcher Integer beim Kreis ist denn gemeint? Radius, Durchmesser, Umfang? Gut, hier könnte man es sich eventuell denken, aber was ist beim Rechteck?

    Auch ein schöner Punkt, der aber durchaus auch in C++ bekannt und gelöst ist. Wenn Du das in C++ so formulierst, wird Dir C++ sagen, dass er eben auch nicht weiß, welchen Integer er hier jetzt reinwerfen soll. Da die Funktion nicht klar festlegt, welchen Integer sie wünscht, also ob Radius, Durchmesser oder Umfang, muss sie ja so allgemein gehalten sein, dass ihr ein beliebiger Integer zur Verarbeitung reicht.
    Und da haben wir natürlich Auswahl und müssen uns selbst festlegen.
    In C++ müsstest Du Dich über ein Casting eindeutig ausdrücken. Da sieht in C++ unschön aus, ist aber an der Stelle das Gleiche wie this->Radius: eine Addition auf die Adresse auf die this zeigt.

    ipsec schrieb:

    Das „is-a“, was immer so runtergebetet wird, hat ja einen Grund: A is-a B ⇒ A kann wie ein B behandelt werden. Und ich sehe hier nicht, dass ich einen Kreis oder ein Rechteck wie einen Integer behandeln kann.

    Bitte die Bibel weglegen und das beten einstellen. Die Gedanken sind frei! 😉

    "is-a" ist nur ein Konzept, dass mit Vererbung beschrieben wird. Stell Dir Kreis als Ableitung von RadiusInterface vor. Damit ist Kreis ein "GetRadiusFunctionProvider" und das wiederum ist ein Integer. Und jetzt lassen wir den ganzen Unsinn weg wieder und sagen "einfach":

    printInteger( this->Radius::Value );
    oder
    printInteger( this->Umfang::Value );

    Wohlgemerkt: Das ist C++... die Sprache, die für Member ausgelegt ist und ich behaupte, dass Mehrfachvererbung und Member erstmal beide die gleiche Aufgabe erfüllen. Das heißt nicht, dass die Syntax in C++ wunderschön ist, noch dass ich ausschließlich alles public machen will. Ich will nur Vorteile von Mehrfachvererbung allgemeiner nutzen.

    Trotzdem: das war bisher eins der besten Postings, die zu dem Thema jemals gekommen sind.



  • Xin schrieb:

    Eine Klasse Radius darf diese Operationen also nicht öffentlich erben, wenn sie Einschränkungen macht.

    OK. Jetzt verwirrst du mich.
    Kannst du dein Rechteck/Kreis Beispiel dann nochmal herzeigen mit protected Vererbung?

    Weil bis jetzt bin ich von public Vererbung ausgegangen...



  • -.- bitte löschen, danke



  • Xin schrieb:

    Fakt ist, dass ein Klasse, die eine Einschränkung ihrer Basis hat, natürlich nicht public erben darf.

    Und private auch nicht.
    Deswegen schrieb ich mein Beispiel so, daß der Mensch tot wird aus einer Mensch::-Funktion aufgerufen! Auch privat haben wir den Ärger, wenn man Konzepte vermischt. Plötzliches Frühableben ist ehrlich suboptimal.

    Zugegeben, C++ ist schwach und unterstützt native weder Mixins noch Interfaces.

    Du solltest nicht den Fehler begehen, das zu ignorieren. Mach doch Sprachmittel dafür. Du kannst es. Für beide!



  • @Xin: Ich befürchte du missbrauchst Vererbung für eine Art von Typsicherheit die man einfacher, besser und "richtiger benannt" haben kann.
    Ein Quadrat ist nunmal keine Seitenlänge, und genau das sagt aber deine Vererbung aus. Ein Quadart "HAT" eine Seitenlänge, es besitzt eine Seitenlänge.

    Wenn du eigentlich sagen willst, "dieser int ist vom Typ Seitenlänge und hier dürfen damit nur Werte >0 eingetragen werden und ich kann die Seitenlänge nur mit einem expliziten Cast auch als Radius in einen Kreis schreiben" dann willst du nict Vererbung sondern etwas anderes.

    BTW: Mit Vererbung könntest du irgendwie niemals ein Quadrat auf die Seitenlänge eines Radius setzen, oder? Schreibt man da dann tausende Konvertierungs-Operatoren in deiner Sprache?

    Ich hoffe auch, dass die Typsicherheit in Server-lastigen Programmiersprachen (also Sprachen wie Java/C# und auch C++ (client ja erstmal nur noch JS...)) noch weiter erhöht wird. Aber bitte nicht durch Missbauch von Vererbung.

    Da nimmst du ja quasi eine Krücke die dir C++ dafür bieten kann und baust an der Krücke solange außen dran bis sie in deiner Sprache mehr schlecht als recht kann was du willst, obwohl es viel einfachere Möglichkeiten gäbe.

    Ich kann mich leider nicht mehr zu 100% an meine Haskell-Stunden erinnern, aber afaik bietet das Haskell-Typsystem da einige Möglichkeiten die du suchst...

    (Teilweise geschieht dies btw auch schon in Hochsprachen> durch Attributes für Validierung, etc. Teilweise werden asserts() in Konstruktoren geschrieben, usw., aber ja, die "Hochsprachen" sind leider noch nicht ganz bereit dafür)

    MfG SideWinder



  • Shade Of Mine schrieb:

    Der Ctor von Postleitzahl validiert 12345 korrekt und es passt. Nun addiert add aber 700000 drauf und wir haben eine ungueltige Postleitzahl. Was tust du hier um das zu verhindern?

    Irgendwie scheint mir dieses Gegenargument überhaupt nicht zu passen. Eine Programmiersprache oder auch nur Programmierkonzepte hindern einen Entwickler doch nicht daran, dumme Sachen zu machen? Und Postleitzahl von Integer erben zu lassen ist doch eher eine unsinnige Sache. Zumindest kann ich da jetzt nicht erkennen, inwiefern das als Gegenargument dienen soll?
    Da man PLZ niemals addieren wird, macht es auch absolut keinen Sinn, die von Integer erben zu lassen.



  • Abgesehen davon sind längst nicht alle Zahlen zwischen 0 und 99999 valide PLZ.



  • KuhTee schrieb:

    Abgesehen davon sind längst nicht alle Zahlen zwischen 0 und 99999 valide PLZ.

    Logo. Nur die Postleitzahl 0 ist nicht valide, alle anderen Postleitzahlen sind in Ordnung. Denn wir haben den Operator ! von Integer geerbt.
    Und das ist gut so, denn "erstens ist das Einsparen von Schreibarbeit in meinen Augen besser als Schreibarbeit und zum anderen spart es auch "Lesarbeit"" und eine "ausführliche Umfrage", wo viele Leute und wenige Informatiker und erfahrene Programmierte teilnehmen, würde das auch bestätigen. Wir müssen da unbedingt lernen, "unser Verständnis in Frage zu stellen", dann wird alles gut.

    Was Xin braucht, ist wohl

    class Postleitzahl:mixin Integer{
       promote relational;//<, <=, ==, >=, >, !=
       promote std::hash(Integer);//sind ja gute Hashtable-Schlüssel.
       promote constructors, copy, assign;//Jo, die sind ok
       friend ostream& operator<<(ostream& out,Postleitzahl plz);//to be implemented
    }
    

    Ist es überhaupt wahr, zu sagen "Die Postleitzahl IST eine Zahl?"

    "En Dampfmaschin dat is ene jroße schwarze Raum. Der hat hinten un vorn e Loch. Dat eine Loch dat is de Feuerung. Und dat andere Loch dat krieje mer später."
    Ist das denn wahr? IST die Dampfmaschine ein GoßerSchwarzerRaum? IST die Feuerung ein Loch? Oder spielt uns hier die deutsche Sprache nur einen Streich?

    Mir kommt da so ein Verdacht.



  • Shade Of Mine schrieb:

    Xin schrieb:

    Eine Klasse Radius darf diese Operationen also nicht öffentlich erben, wenn sie Einschränkungen macht.

    OK. Jetzt verwirrst du mich.
    Kannst du dein Rechteck/Kreis Beispiel dann nochmal herzeigen mit protected Vererbung?

    Habe ich bisher nie gebraucht, weil das in C++ einfach keinen Spaß mehr macht.

    #include <iostream>
    
    struct Integer
    {
    public:
      int Value;
    };
    
    struct Radius : protected Integer
    {
    public:
      Radius( int radius )
      { *this = radius; }
    
      Radius & operator = ( int radius )
      { Integer::Value = radius < 0 ? 0 : radius; }
    
      Radius & operator -= ( int radius )   // Kann man machen, muss man aber nicht
      { *this = Integer::Value - radius; }
    
      Integer const & AsInteger() const // operator Integer const & mag C++ hier nicht
      { return *this; }
    };
    
    void func( Integer const & value )
    {
      std::cout << value.Value << std::endl;
    }
    
    void manipulateRadius( Radius & r )
    {
      r -= 1000;           // Radius wird negativ
    }
    
    int main( void )
    {
      Radius r( 45 );
    
    //  func( r );  geht nicht
      func( r.AsInteger() );  // => 45
      manipulateRadius( r );  
      func( r.AsInteger() );  // => 0
    }
    

    Das sieht hier alles furchtbar aufwendig und unpraktisch aus - was es in C++ auch ist, weswegen ich es nicht nutze. Dazu zwei Dinge: erstens kann man Code generieren. Schlussendlich macht C++ nichts anderes.
    Es geht hier nicht darum, dass dies ein Feature darstellen soll, sondern die Möglichkeit aufzeigen, dass Dir die Vererbung hier konzeptionell ein Bein stellt: Tut sie nicht, sie ist mit dem Membern gleichwertig.

    Ich würde hier eher von einem UInteger ableiten, bzw. einem PositiveDouble.

    Elementar ist hier operator =(), er repräsentiert gewissermaßen dem Schreibvorgang auf Integer::Value.

    Und bitte versteh den Text da oben jetzt nicht, als etwas, was ich in C++ im Alltag formulieren würde oder gar schön finden würde. Er beschreibt nur einen Pfad, soweit wie man ihn eben in C++ gehen kann.

    Shade Of Mine schrieb:

    Weil bis jetzt bin ich von public Vererbung ausgegangen...

    In C++ sind wir hier auch in einem Bereich, der nicht mehr schön/akzeptabel formulierbar sind. Packt man das aber in eine leicht verständlichere Syntax, sehe ich durchaus Potential.

    volkard schrieb:

    Xin schrieb:

    Fakt ist, dass ein Klasse, die eine Einschränkung ihrer Basis hat, natürlich nicht public erben darf.

    Und private auch nicht.
    Deswegen schrieb ich mein Beispiel so, daß der Mensch tot wird aus einer Mensch::-Funktion aufgerufen! Auch privat haben wir den Ärger, wenn man Konzepte vermischt. Plötzliches Frühableben ist ehrlich suboptimal.

    Dein beschriebenes Verhalten ist in C++ genauso möglich. Ich kann hier nur KuhTee recht geben: Wer scheiße programmiert, wird scheiße erhalten.

    volkard schrieb:

    Du solltest nicht den Fehler begehen, das zu ignorieren. Mach doch Sprachmittel dafür. Du kannst es. Für beide!

    Ich ignoriere das nicht. Und ich bin auch nicht mit allem zufrieden. Aber es entwickelt sich und es ist mir in jedem Fall den Versuch wert.

    SideWinder schrieb:

    @Xin: Ich befürchte du missbrauchst Vererbung für eine Art von Typsicherheit die man einfacher, besser und "richtiger benannt" haben kann.
    Ein Quadrat ist nunmal keine Seitenlänge, und genau das sagt aber deine Vererbung aus. Ein Quadart "HAT" eine Seitenlänge, es besitzt eine Seitenlänge.

    Hatten wir schon. Ein Quadrat kann auch ein SeitenlängenProvider sein. Ich mag diese dogmatische Sicht auf 'ist-ein' und 'hat-ein' nicht sonderlich, denn die Unterscheidung ist nur erforderlich, wenn ein Objekt mehrere Unterobjekte gleicher Art besitzt: Mensch hat zwei Beine. Die beiden Beine sind aber nicht identisch. "hat-ein" und "ist-ein" ist eher die Aussage, ob ein implizierter oder expliziter Cast stattfindet. Deswegen mag ich diese Regel nicht, weil sie ein Lösung anbietet für ein Problem, das falsch formuliert wird - es geht eben nicht um Member oder Vererbung, man baut einen Datensatz zusammen. Mit Membern kann man alles machen, muss sich aber explizit ausdrücken, mit Vererbung kann man einige Bereiche abdecken und hat den Cast implizit.
    Vererbung und Member sind das gleiche: man braucht keine zwei Definitionsmöglichkeiten um einen Datensatz zusammen zu stellen. Man muss klarstellen, ob man ein Objekt implizit zu einem Datentypen umwandeln darf, oder nicht.

    SideWinder schrieb:

    BTW: Mit Vererbung könntest du irgendwie niemals ein Quadrat auf die Seitenlänge eines Radius setzen, oder? Schreibt man da dann tausende Konvertierungs-Operatoren in deiner Sprache?

    Eher die explizite Aufforderung, den Radius in eine Seitenlänge zu interpretieren. Ist ein Radius ein Integer und die Seitenlänge auch, so werden beide einen expliziten Konstruktor für Integer haben. Seitenlänge( radiusObjekt ) ist ein expliziter Wunsch des Entwicklers.

    SideWinder schrieb:

    Da nimmst du ja quasi eine Krücke die dir C++ dafür bieten kann und baust an der Krücke solange außen dran bis sie in deiner Sprache mehr schlecht als recht kann was du willst, obwohl es viel einfachere Möglichkeiten gäbe.

    Nein, ich nutze die Krücke, die mir C++ bietet, um die Gleichheit zu demonstrieren und den impliziten Cast zu nutzen und die Definition von Vererbung auszutesten, wenn es keine gleichartigen Member gibt.

    Nun möchte ich die Sprache so formulieren, dass sie den Entwickler dazu verleitet, diesen Weg mitzugehen und mehr Typen beschreibt.


Anmelden zum Antworten