Verleitet C++ zum komplizierteren denken?



  • JustAnotherNoob schrieb:

    Du hättest deinen Spaß mit Python, die haben Datenkapselung durch Umbenennung der Member realisiert *huch* Trotzdem ist die Sprache objektorientiert 🙄

    mit dem Unterschied, daß Python eine betont lockere Sprache ist, die möglichst wenig verbieten und viel erlauben will - eine antiautoritäre Programmiersprache sozusagen 😃

    da paßt es doch nicht schlecht ins Konzept, wenn "private" oder "public" eine Sache freiwilliger Vereinbarung ist ...

    antiautoritär ist mein C++ Compiler hingegen aber ganz und gar nicht - einerseits meckert er jeden kleinen Typfehler bei mir an - andererseits compiliert er Objekte, deren Destruktor ich in einer while-Schleife beliebig oft hintereinadner ausführen könnte. Gute Pädagogik sieht anders aus 🙄 😃



  • volkard schrieb:

    Man hat sich nämlich Gedanken gemacht, wie man in C++ die bösesten Fallstricke von C entschärft.

    hätte man das wirklich getan, dann hätte man z.b. casts sehr eingeschränkt, array- und pointerzugriffe mit 'ner bereichsüberprüfung versehen, eingebauten datentypen feste längen spendiert, den void* abgeschafft, usw. aber nein, es musste ja mit aller gewalt C-kompatibel sein. *rolleyes*
    🙂



  • //fricky schrieb:

    hätte man das wirklich getan, dann hätte man z.b. casts sehr eingeschränkt, array- und pointerzugriffe mit 'ner bereichsüberprüfung versehen,

    Ist es nicht so, daß es überprüfende Container und Iteratoren schon lange gibt? Es muß gar kein Sprachmittel sein.

    eingebauten datentypen feste längen spendiert,

    Nimm doch <stdint.h> oder bau Dir was eigenes.
    Es muß gar kein Sprachmittel sein.

    den void* abgeschafft

    Wer benutzt in C++ denn void*?
    Das muß gar nicht weg, weil man void* praktisch nicht verwendet. Gerademal für eigene Implemetierungen von new/delete.

    , usw.

    Das lese icgh jetzt als
    "Nichts, usw."

    aber nein, es musste ja mit aller gewalt C-kompatibel sein. *rolleyes*
    🙂

    Ja, das Festhalten an der C-Syntax ist der Hauptgrund für die Kompliziertheit der Sprache. Aber wohl auch der Hauptgrund der starken Verbreitung.



  • was steht denn da? bestimmt sowas wie: mach das nicht, lass dieses, um himmels willen nein!, gefährlich, pass auf, usw...

    Das C++ eine Sammlung aus 4 Programmiersprachen ist: C, OOP-C++, Template-C++ und STL und man es als solche betrachten soll.
    Mir fällt immer wieder auf, dass du denkst, C++ würde sich wie C programmieren, wenn du es mit Java vergleichst, oder es würde sich wie Java programmieren, wenn du es mit C vergleichst, das ist Blödsinn.
    C++ hat High-Level _und_ Low-Level. Du musst niemals mit Low-Level Speicher arbeiten, du kannst deine Arrayindices btw. checken lassen, indem du vector nimmst und die at Methode verwendest, dann schmeißter ne Exception.

    aber nein, es musste ja mit aller gewalt C-kompatibel sein. *rolleyes*

    Man nehme Low-Level und Konzepte um aus Low-Level High-Level zu machen, das ist C++. Das ist der Grund für die Flexibilität von C++. Wenn du gleich High-Level nimmst sieht es syntaktisch schöner aus und ist einfacher, aber wesentlich unflexibler.

    antiautoritär ist mein C++ Compiler hingegen aber ganz und gar nicht - einerseits meckert er jeden kleinen Typfehler bei mir an - andererseits compiliert er Objekte, deren Destruktor ich in einer while-Schleife beliebig oft hintereinadner ausführen könnte. Gute Pädagogik sieht anders aus

    Was dein C++ Compiler ist tut nichts zur Sache, schreibe eine Warnung oder gar einen Fehler dafür, wenn es sich um den GCC handelt und submitte den. Wenn es nichts kostet werden sie es annehmen(kosten im Sinne von Performance). Der C++ Standard erlaubt das mehrfache ausführen von Destruktoren jedenfalls nicht. C++ an sich ist ähnlich wie das was du bei Python siehst, es wird nichts verboten, nur geschützt.



  • u_ser-l schrieb:

    antiautoritär ist mein C++ Compiler hingegen aber ganz und gar nicht - einerseits meckert er jeden kleinen Typfehler bei mir an - andererseits compiliert er Objekte, deren Destruktor ich in einer while-Schleife beliebig oft hintereinadner ausführen könnte.

    Das hat auch seine Richtigkeit. Schon mal von Turing gehört? Halteproblem und so?



  • Rhabarber Rhabarber Rhabarber Rhabarber Rhabarber Rhabarber Rhabarber Rhabarber!!! Rhabarber Rhabarber Rhabarber? Rhabarber Rhabarber Rhabarber Rhabarber. Rhabarber Rhabarber, Rhabarber Rhabarber Rhabarber Rhabarber Rhabarber!



  • audacia schrieb:

    u_ser-l schrieb:

    antiautoritär ist mein C++ Compiler hingegen aber ganz und gar nicht - einerseits meckert er jeden kleinen Typfehler bei mir an - andererseits compiliert er Objekte, deren Destruktor ich in einer while-Schleife beliebig oft hintereinadner ausführen könnte.

    Das hat auch seine Richtigkeit. Schon mal von Turing gehört? Halteproblem und so?

    um das zu konkretisieren: Mathematiker haben bewiesen, dass man im allgemeinen Code nicht analysieren kann. Um also zu wissen, was ein Code macht, muss man ihn ausführen. Und schon hast du die Antwort darauf, warum der Compiler sowas nicht anmeckert: es ist schlicht nicht möglich.



  • zur Laufzeit sollte trotzdem eine Beschwerde kommen, wenn versucht wird, eine Methode oder gar den D.tor eines Objekts aufzurufen, welches bereits Destruiert worden ist. Das gehört sich eigentlich so.

    Probiere ich das selbe übrigens mit string statt char*, dann kracht es zur Laufzeit, mit gutem Grund. Immerhin.



  • u_ser-l schrieb:

    zur Laufzeit sollte trotzdem eine Beschwerde kommen, wenn versucht wird, eine Methode oder gar den D.tor eines Objekts aufzurufen, welches bereits Destruiert worden ist. Das gehört sich eigentlich so.

    Sagmal, redest Du von solchem Code?

    string s;
    for(;;)
      s.~s();
    

    Falls ja, muß ich das Ansinnen nicht gerade ernst nehmen.

    Soche Sinnlostests kann der Programmierer aber jederzeit einbauen, zum Beispiel

    class SLTK
    {
      private:
        char status;
      public:
        SLTK():status('c'){};
        SLTK(const SLTK&):status('c'){};
        ~SLTK(){
          assert(status=='c');
          status='d';
        }
    };
    

    und dann davon erben. Aber das gehört nicht in die Sprache rein.



  • führt das vom C++ compiler erzeugte binary zur Laufzeit nicht Buch über die Objekte? Dann könnte es leicht selbst herausfinden, ob der Destruktor eines Objektes schon mal aufgerufen wurde und einen erneuten Aufruf anmeckern.



  • u_ser-l schrieb:

    führt das vom C++ compiler erzeugte binary zur Laufzeit nicht Buch über die Objekte?

    Nein.

    Dann könnte es leicht selbst herausfinden, ob der Destruktor eines Objektes schon mal aufgerufen wurde und einen erneuten Aufruf anmeckern.

    Ich will aber die Kosten dafür im Normalfall nicht zahlen.



  • wieso klappt dann Rtti ?



  • u_ser-l schrieb:

    wieso klappt dann Rtti ?

    Ok, hat sich geklärt. Funktioniert auch ohne Objekte.



  • u_ser-l schrieb:

    zur Laufzeit sollte trotzdem eine Beschwerde kommen, wenn versucht wird, eine Methode oder gar den D.tor eines Objekts aufzurufen, welches bereits Destruiert worden ist.

    Nein, das ist unnötig. Denn in C++ gibt es nur sehr wenig Situationen in denen man einen Destruktor explizit aufruft. Der wichtigste Anwendungsfall ist als Gegenstück zum "placement new", und dann gibt es ein paar esoterische Fälle, die wirklich nichts für Anfänger sind. Für Anfänger kann man ruhigen Gewissens sagen, daß der explizite Destruktoraufruf generell ein Programmierfehler ist. Die Verwendung des Destruktors läßt sich extrem leicht finden, daher ist das kein Problem in der Praxis. Für die Fälle, in denen man es dennoch braucht läßt die Verwendung leicht über Assertions bzw. bedingt compilierte Invarianten prüfen. In C++ wird verlangt, daß der Softwareentwickler die Designentscheidungen trifft und nicht die Laufzeitumgebung. Das Konzept scheint hier einige zu überfordern. Wer es dagegen lieber strikt haben will, sollte Ada verwenden.

    u_ser-l schrieb:

    Das gehört sich eigentlich so.

    Nein, da hat jemand das Konzept RAII noch nicht einmal im Ansatz verstanden. Man ruft in C++ faktisch nie den Destruktor von Hand auf. Es ist kein C oder Java, bei dem man ständig von Hand Resourcen freigeben muß! Ums Abräumen der Objekte kümmert sich der Compiler. Als Softwarearchitekt muß man nur die richtigen Destruktoren dem System zur Verfügung stellen. Wenn man einen eigenen Destruktor schreibt, muß man dazu die notwendige Sorgfalt walten lassen.

    u_ser-l schrieb:

    Probiere ich das selbe übrigens mit string statt char*, dann kracht es zur Laufzeit, mit gutem Grund. Immerhin.

    Wer mit den C-Altlasten herumspielt, sollte auch das notwendige Wissen besitzen, wie man die Gefahren dieser Altlasten eingrenzt. So ist das kein Problem mehr, und per NDEBUG ist man den Overhead für die Releaseversion los.

    #include <iostream>
    #include <string>
    #include <cstring>
    #include <stdexcept>
    #include <cassert>
    
    class A {
        char* s;
    public:
        A() : s(new char[256]) {
            std::strcpy (s, "bin noch da!");
        }
        // Variante 1 
        void invariant () const {
            #ifndef NDEBUG
            if (0 == this->s) {
                throw std::logic_error ("nullpointer exception");
            }
            #endif
        }
        // Variante 2
        inline void invariant () const {
            assert (this->s != 0);
        }
        ~A() {
            this->invariant();
            delete[] s;
            s = 0;
        }
    
        char const* get() const {return s;}
    };
    
    int main(){
        A a;
    
        a.~A(); // <- macht man in C++ einfach nicht
    
        std::cout << a.get() << std::endl;
    
        a.~A(); // <- macht man in C++ einfach nicht
    
        std::cout << a.get() << std::endl;
    }
    


  • u_ser-l schrieb:

    führt das vom C++ compiler erzeugte binary zur Laufzeit nicht Buch über die Objekte?

    Nein, C++ bringt von Haus aus keinen GC mit, und das ist auch gut so. GCs haben einige sehr häßliche Eigenschaften, die man öfters ganz und gar nicht gebrauchen kann.

    Zudem kann man C++ Objekte auch statisch erzeugen, so daß niemals zur Laufzeit dynamisch Arbeitsspeicher allokiert wird, und trotzdem kann man Klassen und Objekte verwenden.



  • @john Exceptions im Destructor? Was für ein Monster bist du eigentlich? (mal davon ab, dass bereits der Vergleich nach wiederholtem Aufruf des dtors undefiniert ist, zum Beispiel wenn zwischenzeitlich ein neues Objekt auf dem Speicherstück erstellt wurde)



  • otze schrieb:

    @john Exceptions im Destructor?

    Das bewegt sich auf dem Niveau der unsäglichen "Beispiele" von u_ser-l.

    Wenn die Alternative abort (assert ruft das schußendlich auf) ist, ist std::unexpected nicht schlimmer. Im Endeffekt hat man das gleiche Verhalten. Und natürlich ist das Kind schon in den Brunnen gefallen, wenn man den Destruktor zweimal aufruft. Die Invariante würde ich in echtem Code auch nie hineinschreiben, da sie eh überflüssig ist. Ich kann mich jedenfalls nicht daran erinnern, daß ich jemals das Problem hatte, zweimal einen Destruktor aufgerufen zu haben.



  • ~john schrieb:

    Nein, das ist unnötig.

    ein Destruktor, den ich für ein- und dasselbe Objekt so oft aufrufen kann, wie ich will, ist kein Destruktor, sondern am ehesten so etwas wie eine at-exit-Funktion.

    Eine Methode wäre der Destruktor, sobald er sich nicht mehr nach Ende des Objekts aufrufen ließe. Das kann C++ zur Laufzeit aber nicht wissen, da es ja, wie ich bei Volkard lese, von sich aus keine generelle Buchführung über die Objekte zur Laufzeit kennt.

    ~john schrieb:

    Denn in C++ gibt es nur sehr wenig Situationen in denen man einen Destruktor explizit aufruft.

    mag sein. Wenn man alle selten vorkommenden Situationen nicht mehr als Problem auffaßt, vereinfacht sich manches. Auf Kosten der Stabilität und Vorhersagbarkeit.

    ~john schrieb:

    Das Konzept scheint hier einige zu überfordern.

    Ja, das Konzept, von einer echten OOP zu sprechen, obwohl das Laufzeitsystem von sich aus nicht einmal eine generelle Buchführung über die Objekte kennt, dehnt einige Begriffe der objektorientierten Denkweise bedenklich weit aus. 🙂

    u_ser-l schrieb:

    Nein, da hat jemand das Konzept RAII noch nicht einmal im Ansatz verstanden.

    die durchsichtige Strategie "mir fällt kein gutes Gegenargument ein, da werfe ich dem Diskussionsteilnehmer einfach Unkenntnis vor" ist etwa so prickelnd wie ein Karamelbonbon im Rinnstein acht Wochen nach Ende des Kölner Karnevals.

    u_ser-l schrieb:

    Man ruft in C++ faktisch nie den Destruktor von Hand auf.

    eben. Deshalb muß sich C++ ja auch nicht darum kümmern 😃

    u_ser-l schrieb:

    Es ist kein C oder Java, bei dem man ständig von Hand Resourcen freigeben muß! Ums Abräumen der Objekte kümmert sich der Compiler.

    jetzt muß ich aber mal herzhaft lachen. Wozu brauchst du denn in deinem Codebeispiel im K.tor und D.tor new und delete, wenn der Compiler das doch von selbst macht ?



  • volkard schrieb:

    //fricky schrieb:

    hätte man das wirklich getan, dann hätte man z.b. casts sehr eingeschränkt, array- und pointerzugriffe mit 'ner bereichsüberprüfung versehen,

    Ist es nicht so, daß es überprüfende Container und Iteratoren schon lange gibt? Es muß gar kein Sprachmittel sein.

    richtig, aber dieser satz passt nicht zu deinem anderen satz:

    volkard schrieb:

    Man hat sich nämlich Gedanken gemacht, wie man in C++ die bösesten Fallstricke von C entschärft.

    also wenn es kein sprachmittel ist, dann hat man sich in C++ leider doch keine gedanken gemacht, sondern hat es libraries überlassen. libraries, die ähnliches leisten, gibt es auch für C. also hätte man C++ garnicht erfinden brauchen.

    volkard schrieb:

    den void* abgeschafft

    Wer benutzt in C++ denn void*?
    Das muß gar nicht weg, weil man void* praktisch nicht verwendet. Gerademal für eigene Implemetierungen von new/delete.

    und delete**[]**, nicht zu vergessen *grins*

    volkard schrieb:

    , usw.

    Das lese icgh jetzt als
    "Nichts, usw."

    eigentlich alles was in C zu undefiniertem verhalten führt, z.b. shiften von negativen zahlen, usw,usw.
    🙂



  • ~john schrieb:

    Es ist kein C oder Java, bei dem man ständig von Hand Resourcen freigeben muß!

    In java musste nix von hand freigeben, vor allem nicht 'ständig'.

    ~john schrieb:

    Ums Abräumen der Objekte kümmert sich der Compiler.

    soso, der compiler macht das *rolleyes*. doch nicht eher der programmierer, indem er delete/delete[] oder ein } an die richten stellen setzt, oder seine smart/shared/dumb/cool/disgusting-RAII-pointers kunstvoll verwendet?
    🙂


Anmelden zum Antworten