Programiersprache für Anfänger



  • DEvent schrieb:

    Im Gegensatz zu dem Beispiel mit C ist das oeffentliche Interface "beschmutzt" durch private Methoden und Attribute. Das hat zur Folge, dass der C++ Compiler dir zwar eine Fehlermeldung ausgibt, wenn du versuchst auf diese zuzugreifen, aber du musst trotzdem jedesmal dein gesammtes Programm neu uebersetzen, wenn du irgendwas an den nicht oeffentlichen Teil veraenderst. Ein stabiles oeffentliches Interface muss man sich in C++ durch abstrakte Klassen erkaufen, was virtuelle Methoden erzwingt.

    Schon mal was von Pimpl gehört?



  • DEvent schrieb:

    Im Gegensatz zu dem Beispiel mit C ist das oeffentliche Interface "beschmutzt" durch private Methoden und Attribute.

    Das stört mich auch seit langem 😞 (Heißt aber nicht, dass ich "OOC" besser finde :))

    edit @tntnet: Pimpl ist aber auch nicht ohne Aufwand und Einbußen zu haben.





  • DEvent schrieb:

    Im Gegensatz zu dem Beispiel mit C ist das oeffentliche Interface "beschmutzt" durch private Methoden und Attribute.

    Dafür ist es in C dadurch beschmutzt, das jeder nach belieben auf deinen String zugreifen kann. Und davon abgesehen hat jeder bei C++ die Möglichkeit Funktionen im Anonymen Namensraum in der Sourcedatei zu deklarieren.

    Und ansonsten Verweise auch ich auf das Handle-Body Idiom, alias Pimpl.



  • asc schrieb:

    Und ansonsten Verweise auch ich auf das Handle-Body Idiom, alias Pimpl.

    Wenigstens die letzten 3 Posts kann man sich ja noch durchlesen 😉



  • DEvent schrieb:

    In C++ sieht das so aus:

    class String {
        public:
            String();
            ~String();
            String copyString();
            char* getString();
            void setString(char* str);
    // man koennte denken, dass dies genuegen wuerde, aber nein
        protected:
    // viele schoene protected Methoden
        private:
    // noch mehr Hilfs-Methoden, die im Interface ueberhaupt nichts verloren haben
    }
    

    ^^ spontan fallen mir zwei möglichkeiten ein, um das zu umgehen:
    1. mach dir ne basisklasse mit den hilfsfunktionen. dann sieht man zwar noch, dass die klasse erbt, aber nicht die funktionen selber.
    2. freie funktionen mit nem 'static' davor, die man dann aber mit einem 'this' versorgen muss. dann sieht man garnix mehr von den hilfsfunktionen.
    🙂



  • ~fricky schrieb:

    2. freie funktionen mit nem 'static' davor, die man dann aber mit einem 'this' versorgen muss. dann sieht man garnix mehr von den hilfsfunktionen.

    Ist aber wertlos, weil du ja keinen Zugriff auf die privaten Variablen hast.



  • asc schrieb:

    Dafür ist es in C dadurch beschmutzt, das jeder nach belieben auf deinen String zugreifen kann.

    Wenn das ein Problem darstellt, dann wird es nicht durch die geschickte Wahl einer Programmiersprache gelöst.



  • Ist aber wertlos, weil du ja keinen Zugriff auf die privaten Variablen hast.

    die man dann aber mit einem 'this' versorgen muss

    :xmas1:



  • DEvent schrieb:

    Ein Beispiel, wo man ein Feature von OOP sauberer in C programmieren kann als in C++: Kapselung.

    Das ist eher nicht gerade das prominenteste Beispiel für ein OO-Feature. Was tust du in Sachen Typsicherheit? Im übrigen meinte ich einen Link auf die Argumente, die du schonmal gebracht hast und "nicht nochmal erläutern" wolltest. Irgendwelche Quellen über OO in C raussuchen kann ich auch, aber ich habe keine Lust, mir daraus deine Meinung zusammenzudestillieren.

    In C schreibe ich die oeffentliche Schnittstelle so:

    extern const void *STRING;
    
    void *createString();
    void deleteString(void* self);
    void* copyString(void* self);
    char* getString(void* self);
    void setString(void* self, char* str);
    

    Alles mit void*. Wahnsinnig sauber.



  • Bashar schrieb:

    Alles mit void*. Wahnsinnig sauber.

    der void* transportiert doch nur ein 'handle'. die funktionen können natürlich erkennen, ob sie ein gültiges string-handle bekommen haben. ausserdem gehört typsicherheit nicht zwingend zu OOP dazu.
    🙂



  • sothis_ schrieb:

    Ist aber wertlos, weil du ja keinen Zugriff auf die privaten Variablen hast.

    die man dann aber mit einem 'this' versorgen muss

    :xmas1:

    Der Zeiger bringt dir doch nix, weil du keinen Zugriff aus der freien (also nicht-Member-) Funktion auf private Member-Variablen hast.



  • Badestrand schrieb:

    Der Zeiger bringt dir doch nix, weil du keinen Zugriff aus der freien (also nicht-Member-) Funktion auf private Member-Variablen hast.

    c++ kennt doch diverse tricks, ich sage nur: 'C++: where friends have access to your private members'
    🙂



  • ~fricky schrieb:

    c++ kennt doch diverse tricks, ich sage nur: 'C++: where friends have access to your private members'

    Dann musst du aber wieder alle Hilfsfunktionen in der Klassendeklaration auflisten (eben als friends), genau das sollte doch vermieden werden.



  • Badestrand schrieb:

    ~fricky schrieb:

    c++ kennt doch diverse tricks, ich sage nur: 'C++: where friends have access to your private members'

    Dann musst du aber wieder alle Hilfsfunktionen in der Klassendeklaration auflisten (eben als friends), genau das sollte doch vermieden werden.

    okay, dann war das ein mist-vorschlag. vergesst ihn einfach.
    🙂



  • Bashar schrieb:

    Alles mit void*. Wahnsinnig sauber.

    Das ist ja kein grundsätzliches Problem; siehe DECLARE_HANDLE().



  • Man will in der OOP Subtyping haben, d.h. Objekte einer abgeleiteten Klasse sollen dort eingesetzt werden können, wo eine Basisklasse erwartet wird. Mit void* kann man überall Objekte jeden Typs einsetzen, das ist zu allgemein. DECLARE_HANDLE kapselt m.W. void* in eigene Strukturen(?), so dass die Typen alle disjunkt sind, das Subtyping geht also verloren.



  • naja OOP is ja nicht das einzige an C++ was 'schön' ist.
    mach mal template metaprogrammierung mit C (ohne makros natürlich!)



  • Bashar schrieb:

    DECLARE_HANDLE kapselt m.W. void* in eigene Strukturen(?), so dass die Typen alle disjunkt sind, das Subtyping geht also verloren.

    Ja, das ist natürlich richtig - und bereits ein guter Grund, das C++-Typsystem zu verwenden. Mein Punkt war, daß es durchaus auch typsicher sein kann.

    (Sogar so etwas wie typsichere Vererbung könnte man so simulieren:

    #define DECLARE_HANDLE(name) \
        typedef struct tag##name {}* name;
    #define DECLARE_HANDLE_INHERITED(name, _base) \
        typedef struct tag##name { _base base; } name;
    

    Aber spätestens da sollte man der Wartbarkeit zuliebe C++ verwenden.)



  • DEvent schrieb:

    Was hat bitte RAII mit Resourcen-Verwaltung zu tun? Im Destruktor sollte man keine externe Resourcen freigeben, denn Destruktor kann nichts im Fehlerfall machen.

    Destruktoren sollten so gestaltet sein, daß sie niemals fehlschlagen und das Programm in einem definierten Zustand belassen. Es ist in C++ absolut unsinnig einen Destruktor einen Exception werfen zu lassen, daß dies beim Stack Unwinding dann logischerweise zum Werfer einer Exception in einem weiteren Destruktor führen dürfte, und dies instantan mit unexspected() endete.
    Fazit: In C++ kann man seine Destruktoren gleich "throw()" definieren, da alles andere keinen Sinn ergibt.

    DEvent schrieb:

    Eine einfache close()-Methode reicht aus.

    filehandler = new Handler();
    // ...
    filehandler.close();
    

    Dein Code führt direkt ins Resourcenleck! Denn wenn im "..." eine Exception geworfen wird, dann wird close niemals ausgeführt!
    Daher müßte das analog zu unten formuliert sein.

    filehander = new Handler ();
    try {
        // Code, der eine Exception werfen kann
    }
    finally {
        Handler.close();
    }
    

    Daher ist eben das "finally" Statement so wichtig. Bei einer Sprache mit RAII müssen dagegen die Destruktoren entsprechend gestaltet sein. Das führt uns dann zu folgendem Code

    {
        Handler filehandler ();
    
        // Code, der eine Exception werfen kann
    } // <- filehandler wird hier zerstört, egal ob eine Exception geworfen wurde oder nicht
    

    Hier brauche ich kein explizites finally Statement, damit der Handler sauber abgeräumt wird. Es ist lediglich erforderlich, daß der Destruktor fehlerfrei funktioniert.


Anmelden zum Antworten