Pointer wegen möglichen Stackoverflow?
-
@Finnegan Ich nutze nicht besitzende Pointer zwar auch noch, aber mit
std::optional
undstd::reference_wrapper
gibt es da ja auch schon Abstraktionmöglichkeiten, die sowas weg kapseln können.
-
@Schlangenmensch sagte in Pointer wegen möglichen Stackoverflow?:
@Finnegan Ich nutze nicht besitzende Pointer zwar auch noch, aber mit
std::optional
undstd::reference_wrapper
gibt es da ja auch schon Abstraktionmöglichkeiten, die sowas weg kapseln können.Kann man machen. Ich frage mich aber, ob das dann aber nicht irgendwann Overkill ist. Solche Objekte sind ja schon etwas umständlicher zu verwenden als ein einfacher Pointer.
Zugegeben, so ein
std::optional
wirft natürlich eine Exception, wenn ich auf einen nicht gesetzten Wert zugreifen will. Das ungeprüft zu tun ist aber m.E. ohenhin ein Programmfehler (was ich normal vialassert
abfange), der meinentwegen auch mit einem Zugriffsfehler wegen Null-Pointer-Dererferenzierung aussteigen darf (auch wenn das streng genommen UB ist).Welche anderen Probleme kann damit vermeiden? Versehentliche oder fehlerhafte Pointer-Arithmetik? So ein
T*
wäre bei mir jedenfalls immer nur ein Objekt. Wenn ich mehrere haben will, dann verwende ich generischebegin
/end
-Iteratoren oder neuerdings auch Ranges.Ich sage nicht, dass
std::optional
hier schlecht wäre, aber irgendwie will ich da auch nicht ganz auf Pointer verzichten, wegen der simpleren Anwendung. So einstd::optional<std::reference_wrapper<T>>
ist schon ganz schön viel "Code-Rauschen", und wie gut das vom Compiler dann tatsächlich in Pointer-äquivalenten Code optimiert wird, müsste ich erstmal noch recherchieren
-
Im Gegensatz zu einem rohen Zeiger und
std::reference_wrapper
besitztstd::optional
das gehaltene Objekt. Um den Zeiger/Wrapper benutzen zu dürfen muss das Objekt noch irgendwo leben, das ist beistd::optional
nicht der Fall. Ein gutes Einsatzgebiet fürstd::optional
sind zB Datenbankfelder, wo der Wert auch mal (null) sein kann, das kann man mit rohen Zeigern oderstd::reference_wrapper
schlecht modellieren.
-
Ich verfolge sehr interessiert diese Diskussion und habe eine Frage. Welches Konstrukt würde folgende Anforderung ggf. besser erfüllen:
Eine Methode einer Klasse "ermittelt" ein Objekt (im konkreten Fall ein Objekt aus einer Datenbank) variabler Größe (kann ein Bild unbekannter Größe enthalten) und "liefert" dieses an eine aufrufende Instanz. Es können keine Annahmen getroffen werden, wie lange die aufrufende Instanz das Objekt "braucht".
In alten Versionen habe ich der liefernden Methode als Parameter eine Referenz auf einen Zeiger geliefert und die liefernde Methode hat ein spezielles Objekt auf dem Heap erstellt und in der Referenz auf den Zeiger zurückgeliefert. In der Doku (in der Hoffnung dass diese gelesen wird ) habe ich darauf hingewiesen, dass die Verantwortung für das gelieferte Objekt bei der aufrufenden Instanz liegt.
In den neueren Versionen wird von der liefernden Methode das zu liefernde Objekt in einem std::shared_ptr geliefert und das Problem mit dem zerstören des gelieferten Objektes scheint behoben zu sein.Zusätzlich stellt sich mir die Frage; wenn man z.B. mit Bibliotheken wie Qt arbeiten möchte oder muss, ist es da nicht von Vorteil wenn man über die Bedeutung von Zeigern Bescheid weiß?
-
ich würde ein solches Objekt in Containern der STL anlegen, z.B. als
map
womit man auch den wahlfreien Zugriff hat. komplexere Resultsets lege ich als Slice in einer Liste an (also eine liste mit mapslist < map<string,string> > slice;
) eine solche Struktur passt zu allen Template-Engines, also in Hinblick auf eine weitere Verarbeitung und Darstellung der Daten. Weitere Möglichkeiten sind JSON-Strukturen auch dafür gibt es Libraries in C++.MFG
-
@SeppJ sagte in Pointer wegen möglichen Stackoverflow?:
Es kann aber gut sein, dass du als high-level Programmierer damit nie in Kontakt kommst, weil eben alle Dinge, die Zeiger brauchen, diese intern benutzen und die Notwendigkeit vor dem Anwender wegkapseln. Das ist ja gerade der Fehler von KK27s Kollegen, dass er meint, das besser zu wissen, und sich damit nur das Leben selber schwerer macht.
Meinst du den Kollegen der nicht von Smart Pointern überzeugt ist oder der Kollege der alles mit new und Pointern macht?
@Peter-Viehweger sagte in Pointer wegen möglichen Stackoverflow?:
naja nicht nur sich, oder?
Wenn man Klassen von Kollegen verwendet, die noch mit Char Arrays mit fester Größe arbeiten und diese dann überlaufen, weil sie nicht mit std::string arebiten und das bei neuen Klassen .
@Peter-Viehweger sagte in Pointer wegen möglichen Stackoverflow?:
ja solche alten sturköpfe, die "das schon immer so gemacht" haben sind echt furchtbar.
Die dann solche Argumente auch bei neuen Klassen und Funktionen verwenden und nicht davon abkommen wollen. Man könnte ja etwas kaputt machen oder neue Fehler entdecken. Was früher schon funktioniert hat, funktioniert ja heute auch noch....
-
@Helmut-Jakoby sagte in Pointer wegen möglichen Stackoverflow?:
... In alten Versionen habe ich der liefernden Methode als Parameter eine Referenz auf einen Zeiger geliefert und die liefernde Methode hat ein spezielles Objekt auf dem Heap erstellt und in der Referenz auf den Zeiger zurückgeliefert. In der Doku (in der Hoffnung dass diese gelesen wird ) habe ich darauf hingewiesen, dass die Verantwortung für das gelieferte Objekt bei der aufrufenden Instanz liegt.
Das ist natürlich "C-Style" und keine gute Idee. Ich finde es jedes mal extrem anstrengend, wie sorgfältig man bei C-Bibliotheken die Doku oder gar den Code lesen muss, gerade wegen solcher Sachen. Das lädt regelrecht zu Fehlern ein. Sowas würde ich zumindest immer als Funktionspaar wie
create_object
/destroy_object
implementieren. Dass der Anwenderfree
ähnliches aufrufen muss, erachte ich da als schlechten Stil, vor allem weil es verschiedene Methoden geben kann, mit denen Speicher verwaltet werden kann (malloc
, C++-new
, eigene Speicherverwaltung, Cache-Handle, über die Funktionen der Datenbank-API mit speziellencreate
/destroy
-Funktionen, etc.). Mit so einem Funktionspaar behält die Bibliothek die Kontrolle darüber.In den neueren Versionen wird von der liefernden Methode das zu liefernde Objekt in einem std::shared_ptr geliefert und das Problem mit dem zerstören des gelieferten Objektes scheint behoben zu sein.
Ohne den Code zu kennen klingt
shared_ptr
nach einer guten Lösung. Wenn allerdings nicht erforderlich ist, dass an verschiedenen Stellen im Code eine Referenz auf ein und dasselbe Objekt gehalten werden muss und diese unabhängig voneinander unterschiedlich lange Zugriff darauf benötigen, täte es eventuell auch einunique_ptr
, der noch ein bisschen leichtgewichtiger wäre.Zusätzlich stellt sich mir die Frage; wenn man z.B. mit Bibliotheken wie Qt arbeiten möchte oder muss, ist es da nicht von Vorteil wenn man über die Bedeutung von Zeigern Bescheid weiß?
Selbstverständlich sollte man als C++-Entwickler darüber Bescheid wissen wie Zeiger und auch besitzende Zeiger funktionieren. Wissen immer, aber das so verwenden nur wenn es wirklich einen guten Grund dafür gibt
Qt ist diesbezüglich aber auch sehr speziell. Das hat eine automatische Speicherverwaltung, die ich in der Form noch nirgendwo anders gesehen habe. Da sind nackte
new
ohnedelete
völlig normal, solange die Objekte in die Qt-Hierarchie "eingehängt" sind. Da werden dann automatisch Kindobjekte feigegeben, wenn Elternobjekte zerstört werden. Das ist ein sehr gewöhnungsbedürftiges System bei dem jedem C++-Entwickler, der das nicht kennt, erstmal die Haare zu Berge stehen würden ... das ist aber bei Qt historisch so gewachsen und stammt noch aus einer Zeit vor den C++11-Smartpointern. Duchaus eine Alternative zuunique_ptr
/shared_ptr
und Verwandten.
-
Hi @KK27 ,
wir haben unsere "Programmier-Richtlinien" des Öfteren mal angepasst. Zudem musste jeder Programmcode durch eine "Software-Qualitätssicherung". Verstöße gegen die Richtlinien führten zur Ablehnung .
Darf bei euch jeder programmieren wie er will?
-
@Finnegan sagte in Pointer wegen möglichen Stackoverflow?:
das ist [...] historisch so gewachsen
gleichbedeutend mit: "ich war 20 jahre lang zu faul, den code aufzuräumen"
-
@Helmut-Jakoby sagte in Pointer wegen möglichen Stackoverflow?:
Hi @KK27 ,
wir haben unsere "Programmier-Richtlinien" des Öfteren mal angepasst. Zudem musste jeder Programmcode durch eine "Software-Qualitätssicherung". Verstöße gegen die Richtlinien führten zur Ablehnung .
Darf bei euch jeder programmieren wie er will?Grob gesagt ja. Wir haben zwar Richtlinien, aber die sind total veraltet und führen zu mehr Problemen als Lösungen. Ein jüngerer Kollege hat mal neue und modernere Richtlinien vorgeschlagen, aber man wollte darüber nachdenken und das wars. Außerdem ist das meiste nirgends dokumentiert und wird mündlich übertragen. Dokumentation sei nicht notwendig. Man fragt oder sagt es einmal und dann weiß man es.
Zum Beispiel eine der alten Richtlinien:
In h. alle Defines und Konstanten reinpacken.
Das hpp inkludet das h und das cpp immer das hpp.
Das hpp inkludet alle Klassen die im hpp und cpp benötigt werden.
Das cpp inkludet nur das hpp.Das war ein Beispiel der alten Richtlinie. Der Kollege hat vorgeschlagen das man für jede Klasse ein hpp hat, forward declarations, eums über defines. Aber naja. Wurde nichts. Eine Software-Qualitätssicherung wurde auch vom jüngeren Kollegen vorgeschlagen, aber es kam nie dazu.
Ich und der jüngere Kollege programmieren "moderner". Teilweise sogar mein Chef und Entwicklungsleiter. Aber alte Angewohnheiten lassen sich schlecht ablegen und manche Kollegen wollen unbedingt an der alten Art und Weise festhalten, weil es funktioniert hat.
Unsere Firma gibt es schon sehr lange. Seit über 50 Jahren und unser Hauptprodukt ist auch so alt. Klar gibt es deswegen viele technischen Schulden, aber...