Verleitet C++ zum komplizierteren denken?
-
volkard schrieb:
Aber ich habe gar keine wilden pointer. Das war eher in C üblich, weshalb auch nur Du diese Gefahr überhaupt siehst.
ok, dann verzichten wir eben auf pointer in unserem c++ programm und nehmen einen falschen array-index um unser const-objekt zu zersemmeln. du kannst es drehen wie du willst, in einer 'unmanaged' umgebung und einer programmiersprache die auf rohem speicher arbeitet, können solche bugs immer passieren.
JustAnotherNoob schrieb:
Richtig, hat schonmal jemand fricky über Punkt 1 aus "Effective C++" aufgeklärt?
was steht denn da? bestimmt sowas wie: mach das nicht, lass dieses, um himmels willen nein!, gefährlich, pass auf, usw...
-
//fricky schrieb:
volkard schrieb:
Aber ich habe gar keine wilden pointer. Das war eher in C üblich, weshalb auch nur Du diese Gefahr überhaupt siehst.
ok, dann verzichten wir eben auf pointer in unserem c++ programm und nehmen einen falschen array-index um unser const-objekt zu zersemmeln.
Was läßt Dich annehmen, daß ich Arrays benutze, die Indexgrenzüberschreitungen zulassen?
Es wird nicht funktionieren, mit Deinem Gefrickel in C stichhaltige Argumente gegen C++ zu sammeln. Man hat sich nämlich Gedanken gemacht, wie man in C++ die bösesten Fallstricke von C entschärft.
-
Mit anderen Worten: Man nimmt was frickeliges als Basis und versucht das grundlegende Konzept irgendwie zu reparieren. Eindeutiger könnte man die Frage des OP nicht beantworten.
-
Tim schrieb:
Mit anderen Worten: Man nimmt was frickeliges als Basis und versucht das grundlegende Konzept irgendwie zu reparieren. Eindeutiger könnte man die Frage des OP nicht beantworten.
Ich sehe keinen kausalen Zusammenhang, der die Sache am eindeutigsten machen würde. Soll heißen: schreibe die unumstößliche Beweiskette auf.
-
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)