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



  • Eigentlich koennte ich die Frage direkt zu Bjarne mailen,
    aber vielleicht ist das ja gar nicht noetig ­čśë
    Warum gibt es in C++ keine interfaces?

    C++			     Java
    ->Klassen		   ->Klassen
    ->abstrakte Klassen ->abstrakte Klassen
    			        ->interfaces
    

    (Habe die unions bewusst weggelassen).
    Da ich im Moment keine adaequate Erklaerung dafuer habe,
    wuerde ich sagen, dass Java C++ in dem Punkt etwas voraus hat.
    Was meint ihr dazu?



  • bei c++ benutzt man abstrakte klassen als interface. siehe z.B. COM.

    rapso->greets();



  • Gegenfrage: Welche Vorteile h├Ąttest du durch explizite Interfaces? W├Ąre das Extra an Sprachkomplexit├Ąt es wert?



  • Man k├Ânnte Mehrfachvererbung vermeiden.

    Bye, TGGC (W├Ąhle deine Helden)



  • TGGC schrieb:

    Man k├Ânnte Mehrfachvererbung vermeiden.

    Will ich das?



  • Nein, wenn du schon so fragst. Ist aber trotzdem ein Vorteil von Interfaces, dass man damit Mehrfachvererbung vermeiden kann.
    Laut C++ - Logik ist es ja geil, 872345685 M├Âglichkeiten f├╝r ein Problem zu haben. Deshalb gibt es ja Overloading und default-Parameter, virtuell und nicht-virtuell, usw., obwohl man mit eins von beiden eigentlich immer auskommen k├Ânnte. Aber vielleicht ist mal wirklich gerade eins besser als das andere.
    Es wird sicherlich genauso mal eine Situation geben, wo ein Interface geiler ist als Mehrfachvererbung. Da hab ich ├╝berhaupt keine Probleme mir das vorzustellen, weil es eh nicht un├╝blich ist, Klassen mit ausschlie├člich abstrakten Methoden zu haben, um das Verhalten von Interfaces ein bisschen nachzuahmen.



  • Hi

    war das mit der mehrfachvererbung fr├╝her bei c++ nicht ein kleineres problem in gewissen situationen. bzw f├╝hrt dazu das die verwaltung der Virtual Funktiontabel nicht gerade einfach war.

    In java hat man die probleme einfach umgangen in dem man es erst gar nicht zul├Ąst.

    [edit]

    mir ist grad bei ein bischen ├╝berlegen gekommen das der zus├Ątzliche ben├Âtigte speicher der Elemente das Problem verursacht. gerade bei casten.



  • Optimizer schrieb:

    Nein, wenn du schon so fragst. Ist aber trotzdem ein Vorteil von Interfaces, dass man damit Mehrfachvererbung vermeiden kann.

    Mag sein, ich sehe interfaces einfach nur als verkr├╝ppelte abstrakte Klassen...

    Schlie├člich kann ein Interface nichts besonderes. Und man muss sich nur mal diese Codeverdoppelung ansehen die man dadurch bei Java dauernd hat:

    Es gibt ein Interface Foo und dann gibt es die Klasse AbstractFoo, welche vern├╝ftiges Defaultverhalten implementiert.

    Der Sinn erschlie├čt sich mir leider nicht. Vorallem weil man gerade durch Interfaces ja in Java (nat├╝rlich nur von Anf├Ąngern) Klassen sieht, die 100.000 interfaces implementieren, w├Ąhrend von 3 Sachen gleichzeitig erben in C++ schon fast seltenheitswert hat ­čśë

    Welche Sprache ist hier also einfacher, aus der Sicht eines Anf├Ąngers?

    Deshalb gibt es ja Overloading und default-Parameter,

    Klar, folgendes rult in Java einfach:

    final DBRes res;
      final String name;
      final String type;
      final String upType;
      final String title;
      final String value;
      final String externValue;
      final DBRes reloadQuery;
    
      public ComponentDescriptor(DBRes res, String name, String type, String upType, String title, String value, String externValue, DBRes reloadQuery)
      {
        this.res=res;
        this.name=name;
        this.type=type;
        this.upType=upType;
        this.title=title;
        this.value=value;
        this.externValue=value;
        this.reloadQuery=reloadQuery;
      }
    
      public ComponentDescriptor(DBRes res, String name, String type, String upType, String title)
      {
        this(res, name, type, upType, title, null, null, null);
      }
    
      public ComponentDescriptor(DBRes res, String name, String type, String upType, String title, String value)
      {
        this(res, name, type, upType, title, value, null, null);
      }
    
      public ComponentDescriptor(DBRes res, String name, String type, String upType, String title, String value, String externValue)
      {
        this(res, name, type, upType, title, value, externValue, null);
      }
    

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

    virtuell und nicht-virtuell,

    du vergisst einfach das ZeroCost-Principle.
    Java hat daf├╝r ja final ­čśë Ist das genauso unn├Âtig?

    Ich kann das ganze aber auch lustigerweise umdrehen:
    wozu brauche ich Interfaces wenn ich KLassen habe. Die antwort wird sein: ausdruckskraft, denn ein Interface ist ja doch etwas anderes als eine Klasse.

    Nun ist der Unterschied virtuell/nicht virtuell aber wohl gr├Â├čer als Klasse/Interface, oder?

    Oder zB float und int - wozu? float reicht doch.
    Wozu ├╝berhaupt Klassen? Funktionen tun doch das selbe - man hat halt immer ein switch() ist ja auch nicht so schlimm.
    Wozu ├╝berhaupt *? man kann ja einfach ganz oft + rechnen...
    ne,ne,ne, das geht einfach in die falsche richtung...

    usw., obwohl man mit eins von beiden eigentlich immer auskommen k├Ânnte. Aber vielleicht ist mal wirklich gerade eins besser als das andere.

    Eben. Genau das ist der Punkt:
    Will man eine Sprache die einem alles erlaubt, auch bl├Âdsinn. Oder
    will man eine Sprache die einem keinen bl├Âdsinn erlaubt, und somit auch geniestreichs verbietet.

    Es wird sicherlich genauso mal eine Situation geben, wo ein Interface geiler ist als Mehrfachvererbung.

    Nur ist der Punkt, dass in C++ 1:1 Java interfaces mit abstrakten Klassen machen kannst. Vielleicht haben Java Interfaces irgendwo einen kleinen Unterschied, aber f├╝r die 08/15 verwendung ist der unterschied nicht vorhanden.

    Falls ich etwas ├╝bersehe: nur her damit!



  • Es wird sicherlich genauso mal eine Situation geben, wo ein Interface geiler ist als Mehrfachvererbung

    Definitiv. Interfaces lassen sich n├Ąmlich viel effizienter Implementieren als abstrakte Basisklassen. In C++ f├╝hrt intensive Mehrfachvererbung gerne mal zu "virtual code bloat", unn├Âtig viele vtables mit unn├Âtig vielen RTTI-Objekten. In C++ ist es also in der Regel nicht so sinnvoll das Interface-Segregation-Principle (ISP) explizit ├╝ber Protokoll-Klassen zu implementieren.
    Leite ich z.B. von vier Klassen ab (die logisch eigentlich nur Interface sein sollen), so erhalte ich vier zus├Ątzliche vtables inklusive RTTI-Objekte, sowie vier unterschiedliche vptr pro Objekt und vier verschiedene Adressen, alles eigentlich unn├Âtig.

    Wer eine effiziente Implementation von Interface f├╝r C++ sucht, dem sei ein Blick hierauf empfohlen:
    http://www.kangaroologic.com/interfaces/libs/interfaces/doc/index.html



  • @Hume:

    coole Sache! Hast du die schonmal eingesetzt und ist die Performance wirklich (wesentlich) besser als bei der Realisierung mit virtuellen Funktionen? Oder wirkt sich der Vorteil erst be exessiver Weitervererbung aus? In der Performance Section
    habe ich nur "...sometimes faster..." gelesen.

    Ich hab gerade mal ein bisschen mit einer eigenen C++ Implementierung von Delegates rumgespielt und dort habe ich einen virtuellen Funktionsaufruf, der mich logischerweise eine ganze Menge Performance kostet und den ich einfach nicht los werde. Vielleicht ist das genau das, was ich gesucht habe.



  • TheBigW schrieb:

    @Hume:
    coole Sache! Hast du die schonmal eingesetzt

    Habe eine selbstgeschriebene Version davon mal einegesetzt. Der gr├Â├čte Vorteil ist die geringere Objektgr├Â├če wenn du von vielen Interfaces erbst. Ob du Geschwindigkeitsvorteile hast, h├Ąngt immer von der konkreten Situation ab (wie oft werden die Funktionen z.B. aufgerufen). In kleinen Benchmark-Anwendungen ist man typischerweise ca. 50% schneller.

    Ich hab gerade mal ein bisschen mit einer eigenen C++ Implementierung von Delegates rumgespielt und dort habe ich einen virtuellen Funktionsaufruf, der mich logischerweise eine ganze Menge Performance kostet und den ich einfach nicht los werde

    Delegates mit virtuellen Aufrufen zu implementieren ist problematisch. Zumindest wenn du f├╝r jeden Typ eine Invoker-Klasse erstellst. In diesem Fall hast du wieder "virtual code bloat".

    F├╝r delegates verwendet man in der Regel "type erasure" und thunks.
    1. "Type erasure":
    Du speicherst alle Memberfunktionszeiger ├╝ber einen generischen Memberfunktionszeiger:

    struct Generic;
    typedef void (Generic::*GENRIC_PMF)();
    
    // a combination of an object and a member funtion pointer
    // both stored in a "type erased" manner
    struct closure
    {
        closure()
            : obj_(0)
            , pmf_(0)
        {}
        void* obj_;
        GENERIC_PMF pmf_;
    };
    

    2. Thunk:
    Kleine Funktion die erst Typeinformationen wiederherstellt und dann Memberfunktion aufruft.

    template <class T, class PT, class R, class P1>
    struct thunk_1
    {
        typedef PT pmf_t;
        static R pmf_thunk(const closure& c, P1 p1)
        {
            return (static_cast<T*>(c.obj_)->*reinterpret_cast<pmf_t>(c.pmf_)(p1);
        }
    };
    

    Ein Delegate-Type sieht dann (stark verk├╝rzt) so aus:

    template <class R, class P1>
    class delegate1
    {
        typedef R (*thunk_type)(const closure&, P1 p1);
    public:
        delegate1()
            : thunk_(0)
        {}
    
        template <class T, class PMF>
        delegate1(T* obj, PMF pmf)
        {
            bind(obj, pmf);
        }
    
        // binding
        template <class T, class Y, class R, class AP1>
        void bind(T* obj, R (Y::*pmf)(AP1))
        {
            typedef R (Y::*PMF)(AP1);
            Y* ro = o;
            this->c_.obj_ = ro;
            this->c_.pmf_ = reinterpret_cast<detail::GENERIC_PMF>(pmf);
            this->thunk_ = &yadi::detail::thunk_1<Y, PMF, R, P1>::pmf_thunk;
        }
    
        // invoking
        R operator()(P1 p1) const
        {
            assert(is_bound());
            return thunk_(c_, p1);
        }
    private:
        closure c_;
        thunk_type thunk_;
    };
    

    Das ist nat├╝rlich alles sehr vereinfacht. Wenn du willst kann ich dir eine komplette (VC6-kompatible) Version schicken, die neben (const- und non-const)-Memberfunktionen auch freie Funktionen unterst├╝tzt.

    Wennn es dir allerdings um maximale Performance geht, schau dir mal die
    FastDelegates an:
    http://www.codeproject.com/cpp/FastDelegate.asp

    Nachteil: Der Code dort hat undefiniertes Verhalten.

    Andere Alternativen: boost::function (relativ langsam), Loki::Functor



  • Genau durch den Codeproject Artikel bin ich darauf gekommen, mal selbst herum zu probieren, getreu dem Motto: "Das mu├č doch auch enfacher gehen". Leider wie beschrieben mit m├Ą├čigem Erfolg.
    Mir ging es in erster Linie nichtmal darum, das ich die Performance unbedingt brauche (selbst die Variante mit virtuellen Funktionen w├Ąre f├╝r ein paar kleine GUI - Callbacks noch schnell genug), sondern eher um den Lerneffekt.

    Wenn ich das richtig verstanden habe castest du also beim "Einpacken" in einen allegemeinen Typ, den du dann bei der Ausf├╝hrung in den speziellen Typ zur├╝ckcastest.

    Das ist nat├╝rlich alles sehr vereinfacht. Wenn du willst kann ich dir eine komplette (VC6-kompatible) Version schicken, die neben (const- und non-const)-Memberfunktionen auch freie Funktionen unterst├╝tzt.
    

    Gerne!



  • Shade Of Mine schrieb:

    Optimizer schrieb:

    Nein, wenn du schon so fragst. Ist aber trotzdem ein Vorteil von Interfaces, dass man damit Mehrfachvererbung vermeiden kann.

    Mag sein, ich sehe interfaces einfach nur als verkr├╝ppelte abstrakte Klassen...

    Schlie├člich kann ein Interface nichts besonderes.

    Siehst du while-Schleifen auch als verkr├╝ppelte for-Schleifen?



  • Shade Of Mine schrieb:

    Optimizer schrieb:

    Nein, wenn du schon so fragst. Ist aber trotzdem ein Vorteil von Interfaces, dass man damit Mehrfachvererbung vermeiden kann.

    Mag sein, ich sehe interfaces einfach nur als verkr├╝ppelte abstrakte Klassen...

    Das ist aber eine v├Âllig falsche Vorstellung. Ich kann sie dir nicht ver├╝beln, denn von C++ aus kennst du dieses Konzept zwar, aber die Verbindung zu den Java-Interfaces kannst du vielleicht nicht herstellen (verzeih mir diese Unterstellung), weil es in C++ das nicht als Sprachmittel gibt. Nimm mal std::sort

    std::sort(myvec.begin(), myvec.end())
    

    Was ist das geforderte Interface von std::sort? begin() und end(). Aber du hast kein Sprachmittel daf├╝r. Das w├Ąre in Java ein Interface.
    Jetzt kommt aber der wichtigste Punkt: Es hat mit Vererbung nicht zwangsl├Ąufig was zu tun. Deshalb hat es auch nicht zwangsl├Ąufig was mit einer ABC zu tun.

    Sieh dir C# an:

    struct Point : ICompareable
    {
        int x;
        int y;
    }
    

    structs kennen keine Vererbung, die Dinger sind das, was du glaub ich als PODs bezeichnest. Ein Interface ist nicht mehr und nicht weniger als eine Schnittstelle. In C++ nutzt du im Templates einfach den operator< und es geht halt oder nicht. In C# musst du halt sagen, dass es so etwas wie diesen operator< geben soll. Du bist gezwungen, die Schnittstelle, die du nutzen willst, zu spezifizieren. Mit Vererbung hat das hier an keiner Stelle was zu tun. Es ist lediglich eine Option, Teile eines Interfaces als virtuelle Methode zu implementieren, um die Implementierung redefinieren zu k├Ânnen. Das Implementieren eines Interfaces kann nat├╝rlich schon als ist-Beziehung aufgefasst werden, aber muss keine Vererbung sein.

    Schlie├člich kann ein Interface nichts besonderes. Und man muss sich nur mal diese Codeverdoppelung ansehen die man dadurch bei Java dauernd hat:

    Es gibt ein Interface Foo und dann gibt es die Klasse AbstractFoo, welche vern├╝ftiges Defaultverhalten implementiert.

    Finde ich nicht verwerflich. Du versuchst Interfaces als Basisklassen zu sehen und nennst sie deshalb "verkr├╝ppelt". Das ist aber gar nicht ihr Konzept. Ich muss es nochmal sagen, in C++ gibt es daf├╝r kein Sprachmittel, deshalb musst du ein bisschen umdenken IMHO. ­čÖé
    Aber was mich grad richtig besch├Ąftigt, wo ist hier Codeduplizierung deswegen?

    Der Sinn erschlie├čt sich mir leider nicht. Vorallem weil man gerade durch Interfaces ja in Java (nat├╝rlich nur von Anf├Ąngern) Klassen sieht, die 100.000 interfaces implementieren, w├Ąhrend von 3 Sachen gleichzeitig erben in C++ schon fast seltenheitswert hat ­čśë

    Hmmmm, ein Beispiel w├╝rde hier helfen. Grunds├Ątzlich gibt es noch Subinterfaces, 93476 Interfaces zu implementieren ist sicher kein guter Stil und wohl auch nicht oft n├Âtig.
    Man muss hier IMHO aber eh vorsichtig sein. Klassenhierarchien sehen in Java einfach anders aus.

    Welche Sprache ist hier also einfacher, aus der Sicht eines Anf├Ąngers?

    Java. Du tust ja grad so, als w├Ąren die Probleme, die Mehrfachvererbung mit sich bringen kann, f├╝r Anf├Ąnger leicht zu durschauen.

    Deshalb gibt es ja Overloading und default-Parameter,

    Klar, folgendes rult in Java einfach: [...]

    Du hast das richtige Beispiel gebracht, das rult n├Ąmlich. Auf das gegenseitige Aufrufen von Konstruktoren warte ich in C++ vergebens. Vielleicht muss ich hier umdenken, aber ich liebe das.
    Ok, ernsthaft: Default-Parameter sind doch nicht ohne Nachteil. Du solltest sie nicht redefinieren, die Reihenfolge der Parameter ist nicht beliebig und brauchst du sie wirklich? Nein. Ich sag aber nicht, dass sie schlecht sind, nur, dass man sie nicht braucht.

    Java hat daf├╝r ja final ­čśë Ist das genauso unn├Âtig?

    Nein, final hat nen Sinn, es verbietet das redefinieren. Nicht-virtuell tut das nicht, macht es nur oft zu einer schlechten Idee. Au├čerdem sind final-Methoden in Java nicht automatisch nicht-virtuell. Mit Sicherheit nicht-virtuell sind nur private und static Methoden. Ich hab aber nichts gegen das virtual-Schl├╝sselwort, versteh mich nicht falsch. Es ist ja nicht sinnlos. Ich sage nur, dass es streng genommen nicht n├Âtig ist.

    Oder zB float und int - wozu? float reicht doch.
    Wozu ├╝berhaupt Klassen? Funktionen tun doch das selbe - man hat halt immer ein switch() ist ja auch nicht so schlimm.
    Wozu ├╝berhaupt *? man kann ja einfach ganz oft + rechnen...
    ne,ne,ne, das geht einfach in die falsche richtung...

    Wieso? Ich habe doch selbst gesagt, dass manchmal was anderes geiler ist. Ein Interface ist manchmal geiler als Mehrfachvererbung, aber das willst du irgendwie als einziges nicht so sehen. Und IMHO ├╝bersiehst du auch, das Interfaces nicht nur im Zusammenhang mit Vererbung genutzt werden (siehe oben). Es ist auch manchmal sch├Âner, die Schnittstelle zu spezifizieren, als sie vom Compiler "ausprobieren" zu lassen.

    Eben. Genau das ist der Punkt:
    Will man eine Sprache die einem alles erlaubt, auch bl├Âdsinn. Oder
    will man eine Sprache die einem keinen bl├Âdsinn erlaubt, und somit auch geniestreichs verbietet.

    Und in jeder Sprache ist wohl was anderes als potenziell bl├Âdsinnig angesehen worden. Das wird sich vermutlich auch nie ├Ąndern. Ich finde es nur wichtig, dass man ernsthaft versucht, die Konzepte anderer Sprachen zu verstehen (ja, diese Erkenntnis ist bei mir noch nicht so lange her ­čśâ ).

    Es wird sicherlich genauso mal eine Situation geben, wo ein Interface geiler ist als Mehrfachvererbung.
    Nur ist der Punkt, dass in C++ 1:1 Java interfaces mit abstrakten Klassen machen kannst. Vielleicht haben Java Interfaces irgendwo einen kleinen Unterschied, aber f├╝r die 08/15 verwendung ist der unterschied nicht vorhanden.

    Falls ich etwas ├╝bersehe: nur her damit!

    Ja... Ganz oben habe ich schon Beispiele gebracht. Auch kriegst du bei Mehrfachvererbung oft mehrdeutige Aufrufe (die der Compiler anmeckert), die du bei Interfaces nie hast. Was ├Ąquivalentes zu Interfaces hast du in C++ nicht.

    EDIT: Dieser Beitrag wurde zum letzten mal am 10. Mai gewartet. ­čśë



  • @TheBigW
    Hab das Ganze einfach mal auf meine Web-Seite gestellt:
    http://fara.cs.uni-potsdam.de/~kaufmann/yadi.zip

    Der Code ist aber mehr "proof of concept" als alles andere.



  • Helium schrieb:

    Siehst du while-Schleifen auch als verkr├╝ppelte for-Schleifen?

    Das ist der Punkt.
    Mich hat es gest├Ârt dass davon die rede war, dass soviele M├Âglichkeiten existieren ein Problem zu l├Âsen.

    Nun wozu ├╝berhaupt Schleifen? ein simples if und goto tut es doch auch?

    Und genau das ist IMHO die falsche richtung und etwas was ich nicht verstehe.
    Nehmen wir als Beispiel mal Java her:
    man will die Sprache m├Âglichst einfach machen, klingt logisch. Lustiges Beispiel ist hierbei aber zB keine Default-Parameter, weil man ja ├ťberladung hat.

    Nun ist das f├╝r mich das selbe wie: keine for Schleife, weil man ja while hat (oder umgekehrt).

    Andererseits hat man aber for, while, do while, for(each), etc... genau das finde ich l├Ącherlich. Wenn man so explizit sagt: keine verwirrenden Features dann sollte man sich an Eiffel halten und eben nur ein while anbieten.

    Die Frage ist nur: will man das wirklich?
    ich denke: nein.

    weil manchmal ist eine for schleife besser, manchmal rult ein foreach und manchmal will ich wirklich eine while schleife haben. warum sollte ich also nur ein while wollen?



  • Optimizer schrieb:

    Was ist das geforderte Interface von std::sort? begin() und end(). Aber du hast kein Sprachmittel daf├╝r. Das w├Ąre in Java ein Interface.

    Nur dass man das auch mit einer abstrakten Klasse machen kann ­čśë

    Interface ist nat├╝rlich _aussagekr├Ąftiger_. Keine Frage.

    Du bist gezwungen, die Schnittstelle, die du nutzen willst, zu spezifizieren. Mit Vererbung hat das hier an keiner Stelle was zu tun. Es ist lediglich eine Option, Teile eines Interfaces als virtuelle Methode zu implementieren, um die Implementierung redefinieren zu k├Ânnen.

    Nur das Problem dass ich damit habe ist, dass es manchmal verdammt vern├╝nftige default Implementierungen gibt.

    zB bei c++ der op= - er ruft den op= aller member auf. Super defaultentscheidung, sie reicht meistens aus.

    Nun hast du aufeinmal das Problem, dass IAssignable das nicht macht.
    Du musst also ganz klar trennen und damit in der Schnittstelle der Klasse festlegen, dass du das Defaultverhalten willst, indem du nicht IAssignable implementierst, sondern von AbstractAssignable erbst.

    sowas st├Ârt mich.

    Aber was mich grad richtig besch├Ąftigt, wo ist hier Codeduplizierung deswegen?

    Ich habe 1 Klasse und 1 Interface. Ich muss mich entscheiden von was ich erbe/implementiere.

    Wenn ein Interface ein Defaultverhalten haben k├Ânnte, w├╝rde das Interface reichen. Und ich m├╝sste mich nie entscheiden was ich mache, weil ich problemlos jederzeit die methode 'assign()' dennoch ├╝berschreiben kann.

    Nat├╝rlich hat man dann das 'problem', dass man ein interface implementiert und einfach vergisst die Methode zu ├╝berschreiben wenn man das defaultverhalten nicht will - nur ist das f├╝r mich kein wesentliches argument.

    Wenn ich von AbstractAssignable erbe, dann kann ich quasi nicht mehr assign() ├╝berschreiben, denn dann w├╝rde ich die leute ja verwirren. was aber, wenn ich glaube, dass ich assign() sp├Ąter einmal ├╝berschreiben werde, aber f├╝r die erste version es nicht tue. soll ich von IAssignable erben und die 100.000 methoden so implementieren wie es AbstractAssignable macht und dadurch sinnlose code verdoppelung habe, oder doch lieber von AbstractAssignable erben und sp├Ąter die leute vor den kopf stossen weil ich entweder die Schnittstelle ├Ąndere indem ich direkt von IAssignable erbe oder einfach sage ich bin ein AbstractAssignable dass sich verh├Ąlt als w├Ąre es keins?

    Hmmmm, ein Beispiel w├╝rde hier helfen. Grunds├Ątzlich gibt es noch Subinterfaces, 93476 Interfaces zu implementieren ist sicher kein guter Stil und wohl auch nicht oft n├Âtig.

    Dieses 'Argument' zielt nur auf Anf├Ąnger ab. Hast du noch nie eine Klasse MyJFrame gesehen die alle listener implementiert? F├╝r profis ist es nat├╝rlich egal, weil die diese fehler nicht machen. Aber gerade anf├Ąnger werden dazu verleitet alle interfaces zu implementieren, w├Ąhrend dass zB in C++ nicht der Fall ist.

    Java. Du tust ja grad so, als w├Ąren die Probleme, die Mehrfachvererbung mit sich bringen kann, f├╝r Anf├Ąnger leicht zu durschauen.

    Ganz einfach: ein ANf├Ąnger erbt nicht von 2 Klassen gleichzeitig. wird ihm so oft vorgesagt ­čśë

    nat├╝rlich ist java gesamt gesehen einfacher, keine frage.

    Du hast das richtige Beispiel gebracht, das rult n├Ąmlich. Auf das gegenseitige Aufrufen von Konstruktoren warte ich in C++ vergebens. Vielleicht muss ich hier umdenken, aber ich liebe das.

    klar, das ist ein feature dass super ist.

    Ich sag aber nicht, dass sie schlecht sind, nur, dass man sie nicht braucht.

    wie ein for und ein do while, stimmts?
    nicht alles was 'nicht essentiell' ist, ist nutzlos.
    Beispiel: wozu Generics? das ewige Object hat gereicht.
    Wozu ├╝berhaupt Klassen, in C sind wir auch so gut ausgekommen.
    Wozu schleifen, ich habe ein goto und ein if.
    und wozu labels, ich kann ja einfach dem goto sagen: springe 3 zeilen vorw├Ąrts, etc.

    ganz einfach: weil es manchmal verdammt praktisch ist.

    nat├╝rlich hat man bei einem if-goto/for/while/do while/for(each) das problem: welche schleife nehme ich und viele anf├Ąnger nehmen eine falsche schleife - aber ist es alles in allem f├╝r uns profis nicht besser dass wird die wahl haben?

    Ein Interface ist manchmal geiler als Mehrfachvererbung, aber das willst du irgendwie als einziges nicht so sehen. Und IMHO ├╝bersiehst du auch, das Interfaces nicht nur im Zusammenhang mit Vererbung genutzt werden (siehe oben). Es ist auch manchmal sch├Âner, die Schnittstelle zu spezifizieren, als sie vom Compiler "ausprobieren" zu lassen.

    Keine Frage. Vielfalt ist gut. Nur steht das im krassen gegensatz zu dem rest deines postings.

    ich habe nichts gegen interfaces, nur sehe ich pers├Ânlich den vorteil von "implements interface" auf dem papier. denn wie man zB an Closable in Java sieht, es klappt nicht immer so wie der gedanke war.

    interfaces w├Ąren IMHO _zus├Ątzlich_ zu einer mehrfachvererbung ideal, oder zumindest interessanter f├╝r mich. mich st├Ârt nur dieses "wozu brauchen wir A, B und C, wenn man mit D eh alles irgendwie machen kann" dass man mit D sachen kann, die man weder mit A, B oder C machen kann, ist klar - aber es gibt sachen wo D einfach nicht so gut ist wie B.

    Ich finde es nur wichtig, dass man ernsthaft versucht, die Konzepte anderer Sprachen zu verstehen (ja, diese Erkenntnis ist bei mir noch nicht so lange her ­čśâ ).

    Es gibt genug Sachen die ich an Java verstehe. Lediglich CheckedException und das exzessive Interfacing erschlie├čt sich mir nicht.
    Aber zB die Konzepte der anonymen Klassen, synchronized (wohl eines der tollsten sprachfeatures von java), reflection, etc. finde ich super.

    Und ich habe auch nichts gegen Interfaces in der theorie, lediglich die Java Implementation gef├Ąllt mir nicht.

    Aber darum geht es mir hier nicht. Es geht mir eigentlich um vielfalt: lieber ein unn├Âtiges feature dass niemand verwendet, wie zB std::valarray als dass ich einmal so ein monster wie ComponentDescriptor implementieren muss (wobei es nat├╝rlich auch einen anderen weg geben wird...)

    nochmal: im prinzip geht es mir nicht um Interfaces sondern darum, dass sie angeblich besser sind als Mehrfachvererbung. Sie sind etwas anderes wie Bashar in seiner Signatur so sch├Ân stehen hat "'better' implies different", sie k├Ânnen MI nicht ersetzen. Genausowenig kann MI interfaces ersetzen, dass habe ich durch dein Posting gelernt.



  • Shade Of Mine schrieb:

    Genausowenig kann MI interfaces ersetzen, dass habe ich durch dein Posting gelernt.

    MI kann interfaces nicht ersetzen? das hab ich jetzt nicht so gelesen. hab ich's nur ├╝berlesen?

    also ich versuche es mal. ein interface ist eine basisklasse ohne attribute, die aber pur virtuelle methode hat und damit von den erben die existenz dieser pur virtuellen methoden fordert.

    was k├Ânnen jetzt interfaces mehr?



  • volkard schrieb:

    MI kann interfaces nicht ersetzen? das hab ich jetzt nicht so gelesen. hab ich's nur ├╝berlesen?

    Ich kann es auch falsch verstanden haben, aber so wie ich es verstanden habe:

    Interface sind keine konkreten Klassen, dh, sie existieren im eigentlichen Code nicht wirklich. Sie sind Compilermagie.

    Sprich: wenn ich von einer abstrakten Klasse erbe, ist die Methode foo() automatisch virtual (sonst k├Ânnte ich sie ja nicht ├╝berschreiben) bei interfaces ist das aber etwas anders. Da ein Interface selber nicht wirklich existiert, muss ich lediglich foo() implementieren, was ich aber nicht '├╝berschreibe' sondern lediglich durch Compilermagie gezwungen werde zu implementieren.

    Daraus ergibt sich, dass ich keine 'nutzlosen' vtable eintr├Ąge f├╝r das interface habe.

    Weiters bieten Interfaces die technische M├Âglichkeit von Runtime-Constraints. dh, ich habe in der vtable nur den eintrag 'implements IAssignable' ohne jetzt anderen ballast zu haben, wie zB direkt Methoden von IAssignable oder gar soetwas wie einen Ctor aufruf - und kann durch reflexion dass "implements IAssignable" aber rauslesen.

    Oder auch alles falsch rum ­čś×
    Ich bin irgendwie verwirrt.



  • @Shade
    Ist schon mehr oder weniger richtig. Durch eine ABC bekommst du grunds├Ątzlich schonmal eine zus├Ątzliche vtable. Die ist f├╝r ein Interface aber unn├Âtig, da dort in jedem Slot sowieso nur "not-implemented-here" drin steht.

    Deshalb bietet z.B. der VC das Attribut novtable. Das sorgt daf├╝r, dass die ├╝berfl├╝ssigen vtables von ABCs vom Linker entfernt werden. Damit reduzierst du den "per class overhead".

    Dummerweise bekommt ein Objekt einer Klasse die von mehreren ABCs erbt aber nat├╝rlich auch mehrere vptr, n├Ąmlich einen f├╝r jede zus├Ątzliche Basisklasse.
    Beispiel:

    struct  I1 {
    public:
      virtual ~I1() {}
      virtual void f() = 0;
    };
    
    struct  I2 {
    public:
      virtual ~I2() {}
      virtual void g() = 0;
    };
    
    struct  I3 {
    public:
      virtual ~I3() {}
      virtual void h() = 0;
    };
    
    struct Impl : public I1, public I2, public I3
    {
      void f() {}
      void g() {}
      void h() {}
    };
    

    Impl-Objekte sind auf typischen Compilern 12 Byte gro├č. Obwohl weder Impl noch die Interfaces irgendwelche Daten enthalten. Die 12 Byte stammen einzig aus den drei vptrs. F├╝r MI ist das eine sinnvolle Implementation. Dort besteht ein Objekt aus mehreren Teil-Objekten und hat damit mehrere Objektadressen. Der Compiler muss also sowieso immer ein Offset-Adjust machen. Legt er jetzt die vptr z.B. jeweils an den Anfang der Teil-Objekte braucht der Compiler nur einmal den Offset anpassen und kann dann sehr effizient virtuelle Methoden aufrufen ( (baseAddress + offset)->vptr[funcIndex]).

    F├╝r Interfaces ist dieser Aufwand aber nicht n├Âtig. Ein Interface hat per Definition keine Implementation. Deshalb muss ein Objekt, dass mehrere Interfaces implementiert weder mehrere Adressen noch mehrere vptr besitzen.

    Ein Interface k├Ânnte z.B. einfach als eine Tabelle von Funktionszeigern + einen Objektzeiger implementiert werden. In dem Moment wo du ein Objekt einer Interface-Referenz zuweist, werden dann einfach die Adressen der konkreten Funktionen in die Tabelle kopiert und eine Refernz auf das Objekt gespeichert.
    In Standard-C++:

    class I1
    {
    ublic:
    	I1()
    		: obj_(0)
    		, table_(0)
    	{}
    	template <class T>
    	I1(T& obj)
    		: obj_(&obj)
    		, table_(getTable(static_cast<T*>(0)))
    	{}
    
    	void  f()
    	{
    		table_->f0(const_cast<void*>(obj_));
    	}
    
    private:
    	template <class T>
    	struct Functions
    	{
    		static void  f(void* _this)
    		{
    			static_cast<T*>(_this)->f();
    		}
    	};
    	struct Table
    	{
    		void (*f0)(void*);
    	};
    	template <class T>
    	Table* getTable(T* = 0)
    	{
    		static Table table = {
    			&Functions<T>::f,
    		};
    		return &table;
    	}
    	const void*  obj_;
    	const Table* table_;
    };
    
    struct Impl1 {
    void f() {cout << "Impl1" << endl;}
    };
    struct Impl2 {
    void f() {cout << "Impl2" << endl;}
    };
    
    int main()
    {
    Impl1 impl1;
    Impl2 impl2;
    I1 i1 = impl1;
    I1 i2 = impl2;
    i1.f();    // Impl1
    i2.f();    // Impl2
    }
    

Log in to reply