[C++ vs. Java] Warum hat C++ keine interfaces?



  • Optimizer schrieb:

    Und effizienter!

    Das habe ich indirekt auch geschrieben.

    Das ist überhaupt kein Problem. Implementierung erben -> ableiten
    Schnittstelle erfüllen -> Interface implementieren

    Nur erscheint dass dann in der Schnittstelle, obwohl es ein implementationsdetail ist. Und genau das stört mich.

    Ja, das hat mich da auch genervt. Sowas ist natürlich dumm, aber ich finde es auch nicht so toll, dass ich nen Algorithmus auf etwas anwenden kann, nur weil gerade die Methoden mit der Signatur da sind.

    Ich komme aus einer Welt, da definiert sich ein Objekt durch die Methoden die es hat. Nicht nur bei C++ - ich habe zB schon mit PHP eine 'Klassenhierachie' ohne eine einzige Vererbung aufgebaut.

    Vielleicht machen die was völlig anderes.

    Dito bei Interfaces. Ich kann ein assign() locker so implementieren:

    public void assign(Class other)
    {
      a+=other.a;
      b+=other.b;
    }
    

    und das obwohl ich von IAssignable abgeleitet habe...

    Dieses Argument verstehe ich nicht. Denn wenn ich einen falschen Namen für eine Aktion nehme, dann habe ich einen semantischen Fehler gemacht. Da tut es nichts zur sachen ob ich einfach kreativ war und einen blödsinnigen namen erfunden habe, eine bestehende Methode missbraucht habe oder sonstwas.

    Wie willst du mich daran hindern in add() etwas abzuziehen? nicht wirklich sinnvoll, oder?

    Interfaces bringen diesbezüglich keine sicherheit. Sie bringen hier lediglich den vorteil, dass sie namen standardisieren. Das ist ein schöner effekt, aber dazu braucht man keine interfaces.

    @Helium:
    kannst du mal einen link zu einer dieser sprachen mit schöner erklärung geben?
    Ich habe mixins bisher immer als

    template<class T>
    struct CanA : public T { virtual void A() {} };
    template<class T>
    struct CanB : public T { virtual void B() {} };
    template<class T>
    struct CanC : public T { virtual void C() {} };
    
    class Mixin : public CanA<CanB<CanC> > >
    {};
    

    viel weiter bin ich da noch nicht eingetaucht, aber sprachen die das ganze schöner anbieten (diese templates sind ja eine qual) wären echt interessant.



  • @Shade
    Rubys Mixin-Konzept finde ich z.B. recht hübsch.



  • Shade Of Mine schrieb:

    Dieses Argument verstehe ich nicht. Denn wenn ich einen falschen Namen für eine Aktion nehme, dann habe ich einen semantischen Fehler gemacht. Da tut es nichts zur sachen ob ich einfach kreativ war und einen blödsinnigen namen erfunden habe, eine bestehende Methode missbraucht habe oder sonstwas.

    Wie willst du mich daran hindern in add() etwas abzuziehen? nicht wirklich sinnvoll, oder?

    Interfaces bringen diesbezüglich keine sicherheit.

    Naja, nimm mal ein extremes Beispiel. Du hast eine Toilette und einen BufferedStream. Beides kannst du flushen. Jetzt kann ich also mein geiles C++-Template, was auf ein Objekt flush() aufruft, benutzen. Kann das im Sinne des Erfinders sein? Du hast keinen falschen Namen gewählt, nicht wahr?

    Wenn ich dagegen ein Interface IBuffered habe, dann implementiert mein BufferedStream dieses Interface und ich kann ihn flushen. Ich kann aber die Toilette zum Glück nicht flushen, weil sie nicht IBuffered implementiert. Würde auch keinen sinn machen, weil das flush() einer Toilette nichts mit dem flushen eines Streams zu tun hat.
    Selbst wenn man um 10 Ecken denkt und die Toilette aus irgendeinem Grund ein Interface IBuffered implementieren lässt, wär das immer noch sanitäranlagen.IBuffered und beim Stream io.streams.IBuffered. Man hat hier einfach eine Unterscheidung, die über die Unterscheidung von Methodennamen hinausgeht.

    Du hast natürlich schon Recht, dass eine Klasse und insbesondere die Schnittstelle einer Klasse sich über deren Methoden definiert. Aber die Methodennamen dafür zu verwenden ist halt nicht immer eine gute Idee. 😃 In so einem Fall haben Interfaces schon Vorteile.
    Wenn man es natürlich falsch macht, bzw. vergisst, ein Interface zu implementieren, obwohl man eine solche Methode hat und die auch vom Sinn her diesem Interface entspricht, so wie bei java.net.Socket, dann nervt es, wenn man sie nicht aufrufen kann.



  • Optimizer schrieb:

    Shade Of Mine schrieb:

    Dieses Argument verstehe ich nicht. Denn wenn ich einen falschen Namen für eine Aktion nehme, dann habe ich einen semantischen Fehler gemacht. Da tut es nichts zur sachen ob ich einfach kreativ war und einen blödsinnigen namen erfunden habe, eine bestehende Methode missbraucht habe oder sonstwas.

    Wie willst du mich daran hindern in add() etwas abzuziehen? nicht wirklich sinnvoll, oder?

    Interfaces bringen diesbezüglich keine sicherheit.

    Naja, nimm mal ein extremes Beispiel. Du hast eine Toilette und einen BufferedStream. Beides kannst du flushen. Jetzt kann ich also mein geiles C++-Template, was auf ein Objekt flush() aufruft, benutzen. Kann das im Sinne des Erfinders sein? Du hast keinen falschen Namen gewählt, nicht wahr?

    Das ist kein gutes Beispiel. Zu einem ordentlichen C++ Template gehört mehr als nur ein syntaktischer Vertrag. Jedes Template hat auch einen semantischen Vertrag (auch wenn man den zur Zeit leider hauptsächlich nur über Dokumentation ausdrücken kann - hie könnten Constraints vielleicht wenigstens ein bischen helfen). Ein Template, dass die Nachricht flush an sein Typ-Argument sendet, muss natürlich festlegen, was flush für semantische Bedingungen erfüllen muss. Ein Client muss diese Bedingungen verstehen und einhalten. Das ist ja z.B. beim Überschreiben einer Basisklassenmethode auch nicht anders.

    Aber die Methodennamen dafür zu verwenden ist halt nicht immer eine gute Idee. In so einem Fall haben Interfaces schon Vorteile.

    Imo nicht auf dieser Ebene. Ein Interface bietet ebenfalls keine semantische Garantie (die Semantik legst du in der Doku fest). Du gibst nur die Signatur (statt nur den Namen) der Methoden vor und hast zusätzlich die Möglichkeit das Interface zu bennen.
    Das hindert aber natürlich niemanden daran seine Toilette einfach von IBuffered abzuleiten. Schließlich ist der Spülkasten ja auch eine Art "Puffer".

    Letztlich ist das Ganze die Geschichte mit dem "protecting against murphy vs. protecting against machiavelli". Sprich: Wer Code falsch benutzen will, der schafft das auch. Da schützt dich auch kein Interface.

    Interfaces sind imo letztlich nur die (nötigen) Krücken für statisch geprüfte Sprachen.



  • Shade Of Mine schrieb:

    Es ist einfach nur eine 'struct' dass ein paar Daten beinhalten soll.
    Cool gell?

    warum nicht einfach public variablen? dann isses nicht mehr code als in c++ oder sonst einer anderen sprache. ich mein wenns nur nen struct sein soll...



  • MasterK schrieb:

    warum nicht einfach public variablen? dann isses nicht mehr code als in c++ oder sonst einer anderen sprache. ich mein wenns nur nen struct sein soll...

    ändert nichts an diesen kranken Ctors.

    In der Tat ist es nämlich keine echte 'struct', weil sie nämlich konstant ist. die daten dürfen nachträglich nicht geändert werden.



  • @HumeSikkins: Und dein Code mit den Type Erase und Thunk ist legales C++?



  • undef schrieb:

    @HumeSikkins: Und dein Code mit den Type Erase und Thunk ist legales C++?

    Grundsätzlich ja. Eine ganz kleine Gemeinheit (mal abgesehen von dem VC6-Workaround) ist aber drin 😉



  • Wie wird das mit den mehrdeutigen Aufrufen dort gelöst? Ich erbe jetzt ein A::foo() und implementiere noch das Interface B, was ein konkretes B::foo() hat. Jetzt rufe ich "mein" foo() auf, was wird nun gemacht?

    Hängt wohl von der konkreten Sprache ab, oder?

    In meiner Lieblinssprache wäre es so, dass die Version aus dem Trait die aus der Basisklasse überschreibt.

    kannst du mal einen link zu einer dieser sprachen mit schöner erklärung geben?

    Zum Thema Mixins gibts viele Sprachen (auch ältere). hmm ... gbeta als Beta-Erweiterung, JAM (JAva with Mixins), Scala, ...



  • HumeSikkins schrieb:

    undef schrieb:

    @HumeSikkins: Und dein Code mit den Type Erase und Thunk ist legales C++?

    Grundsätzlich ja. Eine ganz kleine Gemeinheit (mal abgesehen von dem VC6-Workaround) ist aber drin 😉

    Ich dachte Memberfunktionszeiger können unterschiedlich groß sein. (siehe FastDelegate-Artikel auf CodeProject).
    Wenn der Compiler für den generischen Memberfunktionszeiger z.B. 4 bytes reserviert, aber man dann ein Memberfunktionszeiger 8 bytes groß ist müsste es doch schief gehen.
    Was hab ich falsch verstanden? 🙂



  • Achso ich sehe gerade das für unbekannte Sachen immer der größt-mögliche Speicher reserviert wird. 🙂


Anmelden zum Antworten