Oh mein Gott! Ich habe Windows gekillt! ;-)
-
John Doe schrieb:
Mehrfaches deleten fällt nicht auf weils eben keine Problem ist.
Oh, es ist ein verdammt grosses problem. weil deine ablauf logik im arsch ist.
sowas waere praktisch, wenn es beim debuggen auffaelltdu versuchst fehler zu verstecken, indem du die symptome abfaengst - das ist auf dauer nicht gut. denn der fehler ist ja da - auch wenn er sich nicht in einem absturz zeigt.
Wenn ichs nicht tue zerlegts mich irgendwann und irgendwo.
Nein, exakt beim 2. delete
bzw. bei einem zugriff auf den zeiger (man kann ja problemlos debug allokatoren verwenden, somit sind solche Fehler kinderleicht zu finden (weil sie automatisch entdeckt werden).Nur kannst du bei einem NULL Zeiger nicht sagen, ob der Zeiger jetzt NULL oder wild ist - was ungut ist, weil es ja ein gravierender Unterschied ist.
Da find ich meinen
Ansatz beruhigender.Klar, du hast weniger abstuerze.
aber bedenke: nur weil ein fehler (und gerade logikfehler koennen teuflisch sein) sich nicht bemerkbar macht, ist er trotzdem nicht fort.In einem Standardprodukt sollte man defensiv programmieren
und Fehler schon im Ansatz vermeiden.Ja, vermeiden. nicht verstecken.
Fehler beim Zugriff auf nicht mehr
gültige Pointer dürfen gar nicht auftreten sondern müssen im Vorfeld bereits
vermieden werden.Nein. nicht vermieden -> entdeckt.
Wenn deine Zeiger nur funktionslokal sind macht das wenig Sinn. Bei mir kommen
aber durchaus Pointer als Membervariablen vor da behälts du nicht so einfach die
Übersicht, vor allem wenn der Zugriff halt über GUI und Benutzereingaben erfolgt.??
was hat ne GUI damit zu tun?
wenn du dir unsicher bist, schreib halt immer einen kurzen kommentar, dass dieser zeiger ein flag ist.ich fuer meinen teil kann das selber ganz gut ueberblicken. denn kleine klassen sind recht uebersichtlich.
wobei ich ja zeiger sowieso nur sehr ungerne roh anfasse - das problem also eigentlich garnicht habe, dass ein ungueltiger zeiger existiert.
aber dennoch halte ich es fuer fragwuerdig fehler zu verstecken...
delete 0;
ist ja OK - aber
delete wildptr;
eben nicht.denn, wenn man einen wilden zeiger deleten will (den man ja schonmal geloescht hat) hat man einen gravierenden bug in der anwendung, der nicht auffaellt.
anders ist die sache natuerlich wenn du sagst, dass alle zeiger ein flag sind. nur waere mir das zuviel aufwand fuer zuwenig nutzen...
-
Ich kann dir immer noch nicht ganz zustimmen
Wenn ich mehrfach n = 0 schreibe so zeugt das von Paranoia, stellt meines
Erachtens aber keinen Fehler dar. Inbesondere geht dabei nicht schief. Ich
verstecke also keinen Fehler sondern vermeide ihn. Wenn ich bei Benutzung
mit assert abprüfe krieg ich auch definitiv raus ob ich da wo ich einen
gültigen Pointer erwarte auch einen kriege oder ob ich da wo keinen (bereits)
gültigen Pointer haben will versehentlich einen erhalte.Beispielsweise führen beispielsweise die MS COM-Smartpointer Klassen wie CComPtr
automatisch genaus solche Prüfungen aus. Ich nehme mir übrigens nicht allzu oft
MS als VorbildAlls besseres Vorbild halte ich mich dann doch an Steve McConnell "Code Complete"
Set pointers to null after deleteing or freeing them...
Übrigens tritt ein Fehler natürlich nicht erst beim zweiten Delete auf sondern
bei jedem Zugriff auf den inzwischen ungültigen (und ungeprüften) Zugriff auf.mfg JJ
-
Also, ich bin auch ein 0-Setzer. Weiß auch nicht, was daran nicht richtig oder nicht gut sein soll? Bevor ich etwas instanziere, muß ich doch schauen, ob es nicht schon mal passiert ist. Dafür opfere ich gerne einen Taktzyklus, mehr kostet das heute bestimmt nicht...
-
Also irgendwie, stell ich mir deinen Code jetzt ziemlich chaotisch vor.
Normalerweise legt man doch klar die Lebenszeit des Objekts fest. Wenn da mehrmals delete auf den selben Zeiger geht, ist das doch dann ein Logikfehler.
Egal ob es im Programm zu einem Fehlverhalten kommt oder nicht. Es ist einfach was, dass man nicht haben will.du sagst, solche logikfehler sind nicht schlimm, solange das programm noch normal geht. Aber das ist doch ganz großer Mist. Irgendwann schreibt man vor's delete noch ein cout<<"Tschüss" oder etwas das nicht gleich auffällt und irgendwann knallts dann.
Da lasse ich das Programm lieber beim falschen delete sofort abschmieren.
-
ich weiß nicht mehr wer das geschrieben hat aber Bluescreens können noch vorkommen hab selbst mal einen bei einem fatalen Fehler unabsichtlich ausgelöst
-
Ich setze nicht den Pointer auf 0 um möglichst oft ein delete drauf zumachen,
sondern um zu vermeiden das irgendwo in meinem Programm Pointer rumliegen von
denen keiner weiss ob sie ungültig sind oder nicht.
Für mich ist das genau der gleiche Automatismus mit dem ich Pointer auch mit
0 initialisiere. Wenn ein Pointer 0 ist, dann ist er ungültig ansonsten ist er
gültig. Wenn ein Program mehr als 100 nicht triviale Klassen enthält die ausserdem
(oft) zueinander in Bezug stehen dann behälst auch du ganz sicher nicht den Überblick
welcher Pointer zu einem bestimmten Punkt der Laufzeit gültig ist.
Immer in der Annnahme das du Pointer nicht nur funktionslokal verwendest.Was ist an dieser einen zusätzlichen Zeile (p = 0) chaotisch ? Oder meinst du die
Überprüfungen ?mfg JJ
-
John Doe schrieb:
Wenn ich mehrfach n = 0 schreibe so zeugt das von Paranoia, stellt meines
Erachtens aber keinen Fehler dar.wer n=0;n=0; schreibt, der hat ein ernsthaftes problem.
was hat das mit paranoia zu tun? nach n=0; ist n 0. oder schreiben wir statt
n=0; lieber gleichwhile(n!=0) n=0;
dann koennen wir ganz sicher sein
Aber: in C++ zerstoeren wir unsere resourcen aber in einem dtor. wenn ein dtor nun '2x mal aufgerufen' wird, also sich 2 dtors eine arbeit teilen - dann _hast_ du ein problem. denn wie sonst, koennte man 2x delete aufrufen?
Ich
verstecke also keinen Fehler sondern vermeide ihn.Wilder Zeiger und NULL Zeiger sind 2 Komplett verschiedene Sachen...
Ein NULL Zeiger ist ein valider Zeiger, der eben auf nichts zeigt.
ein wilder zeiger (den du immer NULLst) ist ein programmierfehler.sobald er irgendwo auftritt, hast du einen logikfehler im programm. man kann ihn zwar verstecken, indem man einen NULL zeiger aus ihm mach, der Logikfehler bleibt aber erhalten.
und nochmals:
ich bin nicht generell gegen ein 0 setzen von zeigern - aber eben nur dann, wenn sie ein flag sind. aber wenn ich einen zeiger zerstoere, dann ist er tot. sobald ich ihn verwenden will, habe ich einen logikfehler im programm. ausnahmen sind natuerlich, wenn der zeiger selber ein flag ist und 0 heisst "ich zeige gerade auf garnichts".das problem ist, dass du einen illegalen (nicht vorkommenduerfenden) zustand (verwendung wilder zeiger) versteckst. dadurch kannst du dir fehler einfangen.
einigen wir uns wenigstens darauf: dass es keinen mehrnutzen bringt, alle zeiger auf 0 zu setzen.
-
John Doe schrieb:
Was ist an dieser einen zusätzlichen Zeile (p = 0;) chaotisch ? Oder meinst du die
Überprüfungen ?nee, ich meinte die Tatsache, dass bei dir keiner weiß ob der Pointer gültig ist oder nicht.
Das ist für mich Chaos.
In C++ lässt sich die Lebenszeit von Objekten doch gut regulieren, dank Destruktoren.
Ich meine, schon klar dass es in der Praxis vielleicht nicht immer so rosig aussieht, aber generell sollte man das zumindest versuchen und nicht auf gut glück die Zeiger auf Null setzen weil man keinen Plan mehr hat, was das Programm eigentlich so tut.
-
John Doe schrieb:
sondern um zu vermeiden das irgendwo in meinem Programm Pointer rumliegen von
denen keiner weiss ob sie ungültig sind oder nicht.und sobald du diese situation hast, hast du fehler im programm - weil das einfach nicht passieren _darf_.
Für mich ist das genau der gleiche Automatismus mit dem ich Pointer auch mit
0 initialisiere. Wenn ein Pointer 0 ist, dann ist er ungültig ansonsten ist er
gültig.Das ist eine falsche annahme. Ein Zeiger auf 0 ist sehr wohl gueltig. es heisst: ich zeige gerade auf nichts. es heisst aber auch: ich bin voll lebendig.
waehrend ein wilder zeiger heisst "ich bin tod, wer mich anfasst ist necrophil".
Wenn ein Program mehr als 100 nicht triviale Klassen enthält die ausserdem
(oft) zueinander in Bezug stehen dann behälst auch du ganz sicher nicht den Überblick
welcher Pointer zu einem bestimmten Punkt der Laufzeit gültig ist.
Immer in der Annnahme das du Pointer nicht nur funktionslokal verwendest.Warum nicht? die beziehungen untereinander sind ja egal - weil ich leicht an den invarianten der Klasse A erkennen kann, welche Werte der Zeiger haben darf. das ist ein Blick - und da member sowieso private sind, sind mir die beziehungen egal.
Was ist an dieser einen zusätzlichen Zeile (p = 0) chaotisch ? Oder meinst du die
Überprüfungen ?du verstehst es nicht
du ueberspielst damit logikfehler.aber ich glaube ich hoere jetzt auf...
-
Wenn ich einen Pointer als Member habe dann muss das nicht bedeuten das der
Pointer ausschliesslich im Destruktor zerstört wird. Warum nicht als Folge
irgendeiner anderen Operation. Das heist es kann während der Existenz eines
Objekts vorkommen das eine (Pointer)-Member ungültig ist.Aber OK, Einigung wenn du du nicht mehr glaubst das 0-setzen aller Pointer
ein sicheres Zeichen geistiger Verwirrung darstellt.mfg JJ
-
John Doe schrieb:
Das heist es kann während der Existenz eines
Objekts vorkommen das eine (Pointer)-Member ungültig ist.du liest einfach nicht, was ich schreibe
-
John Doe schrieb:
Wenn ich einen Pointer als Member habe dann muss das nicht bedeuten das der
Pointer ausschliesslich im Destruktor zerstört wird. Warum nicht als Folge
irgendeiner anderen Operation. Das heist es kann während der Existenz eines
Objekts vorkommen das eine (Pointer)-Member ungültig ist.ich sach ja, Chaos pur :xmas2:
das 0-setzen aller Pointer ein sicheres Zeichen geistiger Verwirrung darstellt.
ich glaube, das beschreibts ganz gut
-
Ich glaube deine Vorstellung von einem Softwareprojekt ist etwas zu idealistisch.
In einem Team von 30 - 50 Entwicklern die Komponenten erstellen die miteinander
interagieren wirst du diese Wissen um "Pointer ist tot" nicht an jeder Stelle
zur Verfügung haben.Wenn ich in meiner Komponente eine Funktion habe die eine Pointer übergeben bekommt dann prüfe den Wert diese Pointers ab melde falls notwendig, dieses als
Fehler, damit die anderen Entwickler reagieren und Änderungen vornehmen können.Das ich euch meinen Standpunkt nicht darlegen kann, schade
, aber auch kein Weltuntergang. Zumindest machens auch andere Leute so, die lange und erfolgreich
(praktisch) Software entwickeln, daher bleib ich dabei.mfg JJ
-
John Doe: Bloß weil es gemacht wird muss es nicht sinnvoll sein. Wenn man in einem Team arbeitet, dann schrauben in den seltensten Fällen 2 Leute an der gleichen Klasse herum. Das Klasseninterface sollte im Idealfall IMHO so gestaltet sein, dass der Anwender der Klasse nichts mit raw pointers zu tun hat, also erübrigt sich das nullsetzen in den meisten Fällen, da man innerhalb einer Klasse ganz gut überblicken kann wo welcher Pointer wie benutzt wird.
-
John Doe schrieb:
Wenn ich in meiner Komponente eine Funktion habe die eine Pointer übergeben bekommt dann prüfe den Wert diese Pointers ab melde falls notwendig, dieses als
Fehler, damit die anderen Entwickler reagieren und Änderungen vornehmen können.Bitte versuch wenigstens einmal zu lesen und zu verstehen was ich geschrieben habe. das tut mir langsam naemlich echt weh.
niemand ist gegen checks oder defensives programmieren. darum geht es hier garnicht.
wenn jemand einen kaputten zeiger uebergibt (NULL ist ein gueltiger zeiger) dann ist sein code kaputt.
und ich bin dafuer, dass man solche fehler aufdeckt.genau darum geht es. nicht um das checken von fehlern, sondern darum, dass ein NULL zeiger ein gueltiger zeiger ist und ein wilder zeiger nicht.
wenn irgendwo ein wilder zeiger auftaucht, ist der code kaputt. einfach einen NULL zeiger daraus machen, macht die sache nur schlimmer. weil das programm laeuft, als waere alles ok - dabei hat jemand den zeiger aus versehen geloescht.nun kann ein null zeiger aber ein legaler wert sein und es wird weiter gearbeitet, obwohl der aufrufer glaubt einen zeiger auf ein objekt zu uebergeben. der aufgerufene bekommt aber einen null zeiger. er meldet den fehler nicht, weil es durchaus OK sein kann NULL zeiger zu uebergeben (das ist ja einer der unterschiede zwischen referenz und zeiger).
man hat nun einen bug im programm, den man vertuscht.
bei mir wuerde in dieser situationen der debugger anspringen und mir zeigen "hier ist ein ungueltiger zeiger". bei dir laeuft das programm durch und hat zB nur ein andere ergebnis und vielleicht nichteinmal das (beim testen treten solche fehler ja nie zu tage, sondern erst, wenn die software beim kunden ist :()
meine variante ist also die "sichere", weil fehler entdeckt werden (sofern man testet - wenn man nicht testet, sind beide variante gleich mies, weil der fehler unetdeckt bleibt)
-
Bei COM-Programmierung lassen sich Raw-Pointer zumindest als Parameter
nicht vermeiden. Wenn Leute, die seit mehr als 10 Jahren Software entwickeln
und es auch schon zu erfolgreichen Buchveröffentlichungen gebracht haben, soetwas
,inklusive Begründung, empfehlen dann hat das, insbesondere wenn ich diese
Begründung aus eigener praktischer Erfahrung nachvollziehen kann, durchaus Sinn,
für mich.Nochmal ganz zum Anfang Ausgangssituation:
- Eine Klasse mit verschiedenen Member-Variablen, darunter mindestens ein
Pointer
- Eine Memberfunktion wird aufgerufen, diese bewirkt das dieser Pointer gelöscht
wird.
- Eine weitere Memberfunktion wird aufgerufen. Diese benötigt zur Verarbeitung
diesen Pointer.
a.) Beim Löschen wurde der Pointer auf 0 gesetzt. Ein simples assert wird für
den Entwickler oder die QA deutlich anzeigen das hier ein ungültiger
Pointer übergeben wurde.
b.) Beim Löschen wurde der Pointer nicht 0 gesetzt. Eine direkte Überprüfung ist
nicht möglich, die Funktion beginnt ihre Arbeit, beim Aufruf von weiteren
Unterfunktionen wird dieser Pointer als Parameter weitergeben. Irgenwann
einmal knallt es, im günstigsten Fall direkt wenn der Pointer halt ungültig
ist. Aber eben "weit unten auf dem Stack". Aber, praktisch schon
vorgekommen, an der bewussten Speicheradresse
befindet irgendetwas gültiges, nun vielleicht probiert man hiermit dann ein
delete, dann kommt aber richtig Freude auf.Haltet mich für stur, aber mir gefällt Variante a besser.
mfg JJ
-
Soll ich noch auto_ptr und scoped_ptr in die Diskussion einwerfen? Die nullen zwar intern, aber ich finde nichts Falsches daran und habe mit ihnen auch bisher nie Probleme gehabt - auch nicht in komplizierteren Situationen.
-
Hi Shade of Mine,
Mache ich nur, wenn der Zeiger selber state anzeigt. Also zB bei einem smartpointer, wenn zeiger==NULL wird auf nix gezeigt.
würdest du den auch Zeiger auf den Referenzzähler (legt man ja immer am Heap an) eines shared_ptr's auf 0 setzen?
-
John Doe schrieb:
Bei COM-Programmierung lassen sich Raw-Pointer zumindest als Parameter
nicht vermeiden. Wenn Leute, die seit mehr als 10 Jahren Software entwickeln
und es auch schon zu erfolgreichen Buchveröffentlichungen gebracht haben, soetwas
,inklusive Begründung, empfehlen dann hat das, insbesondere wenn ich diese
Begründung aus eigener praktischer Erfahrung nachvollziehen kann, durchaus Sinn,
für mich.Ich habe mein wissen natuerlich vom Penner um die Ecke und noch nie professionell programmiert und auch keine Beispiele und Begruendungen genannt.
a.) Beim Löschen wurde der Pointer auf 0 gesetzt. Ein simples assert wird für
den Entwickler oder die QA deutlich anzeigen das hier ein ungültiger
Pointer übergeben wurde.operator void schrieb:
Soll ich noch auto_ptr und scoped_ptr in die Diskussion einwerfen? Die nullen zwar intern, aber ich finde nichts Falsches daran und habe mit ihnen auch bisher nie Probleme gehabt - auch nicht in komplizierteren Situationen.
Warum verdammt nochmal liest keiner meine Beitraege??
Coward schrieb:
Hi Shade of Mine,
Mache ich nur, wenn der Zeiger selber state anzeigt. Also zB bei einem smartpointer, wenn zeiger==NULL wird auf nix gezeigt.
würdest du den auch Zeiger auf den Referenzzähler (legt man ja immer am Heap an) eines shared_ptr's auf 0 setzen?
Ja, endlich versteht mich jemand.
Natuerlich wuerde ich das - weil der Zeiger einen state reflektiert. 0 bedeutet auf kein Objekt zeigen.gerade bei einer smart pointer klasse _kann_ es in _keiner situation_ zu einem wilden pointer kommen.
ich rede die ganze zeit vom unterschied 0-zeiger und wilder zeiger.
-
Natuerlich wuerde ich das - weil der Zeiger einen state reflektiert. 0 bedeutet auf kein Objekt zeigen.
Und genau diesen state hat, meines Erachtens, jeder Pointer. Unter gewissen
Umständen ist dieser state vernachlässigbar, insbesondere wenn man mit Smartpointern
arbeitet. Wenn man das jedoch nicht kann, oder will, oder was auch immer, dann
sollte diese state, meiner Meinung nach, jederzeit überprüfbar sein.Natürlich ist ein NULL-Pointer erlaubt. Aber in meinen Programmen ist die Logik
nummal üblicherweise so, das wenn ich einen NULL-Pointer bekomme dann ist der
für mich "leer". Und wenn ein Pointer da leer ist wo ich einen "vollen" erwarte
und genauso umgekehrt, dann ist das genau die Fehlersituation die abfangen will.
Und diese Vorgehensweise ist halt nur dann machbar wenn man delete und 0-setzen
zusammen hält.mfg JJ