Wann benutzt ihr direkt Pointer?
-
justchris schrieb:
Wann benutzt ihr direkt Pointer?
Eigentlich nur wenns ne Schnittstelle erfordert oder wenns sich durch Profilen tatsächlich als nötig erweisen sollte, was aber eigentlich nur vorkommt wenn ich über die Pixel eines Bilds renne. Sonst immer per default unique oder shared pointer.
-
Dobi schrieb:
justchris schrieb:
Wann benutzt ihr direkt Pointer?
Eigentlich nur wenns ne Schnittstelle erfordert oder wenns sich durch Profilen tatsächlich als nötig erweisen sollte, was aber eigentlich nur vorkommt wenn ich über die Pixel eines Bilds renne. Sonst immer per default unique oder shared pointer.
Warum? Warum nutzt man das bei Parametern?
void foo(std::shared_ptr<LargeObject> pointer) { if (pointer) { } } // Statt void foo(LargeObject* pointer) { if (pointer) { } }
Wo ist der Vorteil? Man bindet den Aufrufer an einen Zeigertyp, und man bindet die Funktion an Teile der Standardbibliothek, bei unique_ptr bindet man sogar an C++11. Und wozu das Ganze? Es hat doch überhaupt keine Vorteile.
-
Irgendwann könnte das ganze ja multithreadig werden.
Aber da bei sowas würd ich eigentlich eh erstmal eine Referenz nehmen. Ich kann mich grad gar nicht dran erinnern, dass ich in meinen letzten Projekten mal prüfen musste, ob ein Zeiger null ist. Falls das ein Indikator für Design-Probleme ist, flame ruhig. Ich weiß, dass es noch viel für mich zu lernen gibt.
-
Wenn der Zeiger nicht nullptr sein darf, nimmst du natürlich eine Referenz. Wenn nicht, nimmst du halt einen Zeiger. Aber ein shared_ptr oder ein unique_ptr& macht eigentlich aus oben genannten Gründen nie Sinn. unique_ptr ist toll, wenn man auch gleich Besitz übergeben möchte. shared_ptr ist toll, wenn man wirklich shared ownership möchte. (Auch wenn ich das noch nie gebraucht habe.) Aber ansonsten sind "rohe" Pointer und Referenzen doch einfach zu bevorzugen?
-
Achso, klar. Grundsätzlich nehm ich natürlich Referenzen. Wenn die aber nicht gehen, dann shared oder unique pointer. Ich wüsste gerade keine Situation in der Referenzen nicht passen würden und gleichzeitig shared/unique nicht hilfreich wäre. Fällt dir eine ein?
-
Dobi schrieb:
Ich wüsste gerade keine Situation in der Referenzen nicht passen würden und gleichzeitig shared/unique nicht hilfreich wäre. Fällt dir eine ein?
Sogar zwei, wie schon erwähnt: Nicht-besitzende Verweise, die sich ändern oder die ungültig sein können.
-
cooky451 schrieb:
ein [...] unique_ptr& macht eigentlich aus oben genannten Gründen nie Sinn.
Einen
unique_ptr&
hatte ich schon öfters. Das ist sinnvoll, wenn die Funktion den Zeiger umbiegen darf, aber keinen Besitz daran hat.Bei meinem aktuellen Projekt habe ich mir testweise eine Klasse
raw_ptr
geschrieben. Mittlerweile frage ich mich, warum es den noch nirgens gibt, obwohl er doch viele Vorzüge hat:- Zeigerarithmetik und 0 kann verboten, aber Zuweisungen erlaubt werden
- Er verhält sich zu dem normalen Pointer wie die C++-Casts zu den C-Casts: Er hat einen Namen wie ein Schlüsselwort. Dadurch wird der Code sprechender und man kann das Sourcefile nach Vorkommen untersuchen (das ist nicht zu unterschätzen
)
- Die Syntax gleicht der Smartpointern, man weiss sofort, wo das const hingehört)
- Die Klasse eignet sich für assert, logging und solche Sachen
- Man muss nicht mehr von void* casten
Das wichtigste ist, dass man die Ambivalenz des Zeigers von einem Iterator und einer veränderbaren Referenz strikt aufteilen kann: Einmal in Iteratoren und einmal in den raw_ptr. Für die Fälle, wo man wirkliche Pointer braucht (z.B. für Bildbearbeitung), dann kann man diese immer noch nehmen. Aber für die meisten Fälle verwende ich das Paar
unique_ptr
/raw_ptr
. Irgendwie ergänzen sich die ideal.
-
Ja, das hab ich schon verstanden.
Ich bin bisher nur noch nicht in eine Situation gekommen, in der ich es mit nicht-besitzenden Verweisen, die sich ändern oder ungültig sein können, zu tun hatte. Wenn mich C++11-STL-Abhängigkeit nicht stört, und es nichts mit Performance zu tun hat, warum sollte ich dann nackte Zeiger incl. der dazugehörigen Gefahren benutzen? Wenn ein Verweis ungültig werden kann, hat man es mit schlauen Zeigern doch meist leichter. Den pädagogischen Faktor, dass man eventuell nicht mehr drüber nachdenkt, wo man seine Objekte überhaupt zerstört, lass ich mal außer acht. Darüber hab ich mir früher lang genug Gedanken gemacht.
Vielleicht ist es letztendlich auch einfach eine Frage des persönlichen Geschmacks.
-
grux schrieb:
Bei meinem aktuellen Projekt habe ich mir testweise eine Klasse
raw_ptr
geschrieben. Mittlerweile frage ich mich, warum es den noch nirgens gibt, obwohl er doch viele Vorzüge hatWahrscheinlich weil wichtigere Probleme vorhanden sind
Ich hatte tatsächlich schon die selbe Idee, aber vermisst habe ich die Funktionalität noch nicht wirklich. Am ehesten relevant für mich wäre die Möglichkeit, Initialisierung zu erzwingen. Aber ein simples * ist eben doch schöner :p
Viel schlimmer finde ich die Tatsache, dass niemand einen kopierbaren Smart-Pointer braucht. Irgendwie scheint es vielen Leuten zu gefallen, jeweils von Neuem die Big Three zu implementieren und sich mit fehleranfälligem Boilerplate-Code rumzuschlagen.
Dobi schrieb:
Wenn mich C++11-STL-Abhängigkeit nicht stört, und es nichts mit Performance zu tun hat, warum sollte ich dann nackte Zeiger incl. der dazugehörigen Gefahren benutzen?
Weil dir weder
unique_ptr
nochshared_ptr
was bringt, wenn du keinen Besitz zu verwalten hast. Wenn die Situation bei dir nicht vorkommt, ok
-
Dobi schrieb:
dazugehörigen Gefahren
Die da wären? Ausversehen ++ schreiben? Na ja, das ist doch eher unwahrscheinlich. Und ptr*& habe ich auch super selten genutzt, das wäre in der Tat ein Vorteil, quasi eine Referenz auf einen Pointer der nicht 0 sein kann. Dann braucht man aber noch eine Klasse, die wieder 0 sein kann. Nee. So schlimm ist das auch wieder nicht. (Und der Rest der Argumente von grux: Gleiche Syntax wie Smartpointer, man weiß wo das const hin muss? oO Und nach void* casten musste man noch nie.)
Aber ich sage ja auch nichts gegen Smartpointer an sich, unique_ptr nutze ich überall, nur zum übergeben macht es keinen Sinn, wenn man keinen Besitz übergibt. Das Killerfeature von Smartpointern ist halt das sichere delete, aber wenn man das nicht braucht (weil man den Zeiger eh nicht besitzt), dann machen sie einfach keinen Sinn.
-
Nexus schrieb:
Dobi schrieb:
Wenn mich C++11-STL-Abhängigkeit nicht stört, und es nichts mit Performance zu tun hat, warum sollte ich dann nackte Zeiger incl. der dazugehörigen Gefahren benutzen?
Weil dir weder
unique_ptr
nochshared_ptr
was bringt, wenn du keinen Besitz zu verwalten hast. Wenn die Situation bei dir nicht vorkommt, okIn der Situation benutz ich halt ne Referenz.
cooky451 schrieb:
Dobi schrieb:
dazugehörigen Gefahren
Die da wären? Ausversehen ++ schreiben?
Memleaks oder Ungültigkeit. Aus void Foo( Irgendwas* ); geht ja nur mit Kommentar oder Verständnis für das, was passiert, hervor, ob der Besitz auch übergeben wird. Bei void Foo( Irgendwas& ); und void Foo( shared_ptr<Irgendwas> ); sieht jeder direkt, was da los ist.
Für alles, das ich mir mit new hole, benutz ich RAII. Wenn ich dann sicher sein kann, dass das übergebene Objekt am Leben bleibt, übergeb ich es per Referenz. Wenn ich das nicht sein kann (Threadigkeit) per shared_ptr. Bei allen Beispielen, die mir einfallen, bei denen ich ein Objekt ohne den dazugehörigen Besitz übergebe, brauche ich keinen Zeiger, weil ich eine Referenz nehmen kann. Gibt es da irgendeinen Fall (kurzes Codebeispiel wär cool, bei dem void Foo( Irgendwas* ); nötig wäre? Ich schließ ja nicht aus, dass ich nicht irgendeine Anwendung gerade total übersehe.
-
Dobi schrieb:
Aus void Foo( Irgendwas* ); geht ja nur mit Kommentar oder Verständnis für das, was passiert, hervor, ob der Besitz auch übergeben wird.
Nein, eben nicht. Du schreibst ja selbst "Für alles, das ich mir mit new hole, benutz ich RAII." Folglich ist in deinem Code bereits klar, dass
Irgendwas*
kein besitzender Zeiger sein kann.Irgendwas*
ist ein passiver Verweis (eventuell mit der Möglichkeit zuNULL
), die Besitzverhältnisse sind ebenso eindeutig wie mitshared_ptr
oderunique_ptr
.Dobi schrieb:
Memleaks
Trifft auf besitzlose Zeiger nicht zu.
Dobi schrieb:
Ungültigkeit
[...]
Wenn ich dann sicher sein kann, dass das übergebene Objekt am Leben bleibt, übergeb ich es per Referenz.Zeiger und Referenzen verhalten sich gleich, wenn das referenzierte Objekt out of scope geht, daher kannst du die Lebenszeit nicht als Problem von Zeigern darstellen, aber gleichzeitig Referenzen verwenden.
Dobi schrieb:
Gibt es da irgendeinen Fall (kurzes Codebeispiel wär cool, bei dem void Foo( Irgendwas* ); nötig wäre? Ich schließ ja nicht aus, dass ich nicht irgendeine Anwendung gerade total übersehe.
Ich nehme Zeiger gerne, wenn sie
NULL
sein können, um z.B. optionale Werte darzustellen.void SpaceShip::SetTarget(SpaceShip* target); // NULL bedeutet kein Ziel
-
Nexus schrieb:
Dobi schrieb:
Aus void Foo( Irgendwas* ); geht ja nur mit Kommentar oder Verständnis für das, was passiert, hervor, ob der Besitz auch übergeben wird.
Nein, eben nicht. Du schreibst ja selbst "Für alles, das ich mir mit new hole, benutz ich RAII." Folglich ist in deinem Code bereits klar, dass
Irgendwas*
kein besitzender Zeiger sein kann.Irgendwas*
ist ein passiver Verweis (eventuell mit der Möglichkeit zuNULL
), die Besitzverhältnisse sind ebenso eindeutig wie mitshared_ptr
oderunique_ptr
.Klar, ich selbst weiß, dass nakte Zeiger bei mir keinen Besitz übergeben können. Meine Kollegen jedoch vielleicht nicht.
Nexus schrieb:
Dobi schrieb:
Wenn ich dann sicher sein kann, dass das übergebene Objekt am Leben bleibt, übergeb ich es per Referenz.
Zeiger und Referenzen verhalten sich gleich, wenn das referenzierte Objekt out of scope geht, daher kannst du die Lebenszeit nicht als Problem von Zeigern darstellen, aber gleichzeitig Referenzen verwenden.
Stimmt, da sind beide gleich, also kann ichs mir aussuchen, und weil ich Referenzen schöner finde ...
Nexus schrieb:
Dobi schrieb:
Gibt es da irgendeinen Fall (kurzes Codebeispiel wär cool, bei dem void Foo( Irgendwas* ); nötig wäre? Ich schließ ja nicht aus, dass ich nicht irgendeine Anwendung gerade total übersehe.
Ich nehme Zeiger gerne, wenn sie
NULL
sein können, um z.B. optionale Werte darzustellen.void SpaceShip::SetTarget(SpaceShip* target); // NULL bedeutet kein Ziel
Oh, richtig, gutes Argument. An Optionalität hatte ich gar nicht gedacht, weil mich die Nullprüfungen immer so sehr nerven. Bei beispielsweise optinalem Logging lös ich das dann so:
struct Report { virtual void Log( const std::string& msg ) = 0; }; struct ConsoleReport: public Report { virtual void Log( const std::string& msg ) override { std::cout << msg << "\n"; } }; struct DummyReport: public Report { virtual void Log( const std::string& msg ) override {} }; void Foo( Report& report ) { // ... report.Log( "Bla." ); // ... report.Log( "Blubb." ); }
auch wenn es so vielleicht kürzer wär:
void Foo( Report* reportPtr ) { // ... if ( reportPtr ) reportPtr->Log( "Bla." ); // ... if ( reportPtr ) reportPtr->Log( "Blubb." ); // ... }
Ob ich mich bei deinem Spaceship-Beispiel auch irgendwie davor drücken könnte, müsste ich dann überlegen. Bei SetTarget(SpaceShip* target) fühl ich mich aber schon irgendwie wieder unwohl, weil vielleicht erst später drauf geschossen wird, irgendwer anders das Ding aber schon vorher zerbombt (incl dtor-Aufruf) hat.
-
Dobi schrieb:
Klar, ich selbst weiß, dass nakte Zeiger bei mir keinen Besitz übergeben können. Meine Kollegen jedoch vielleicht nicht.
Natürlich, jeder weiß das.
Dobi schrieb:
Stimmt, da sind beide gleich, also kann ichs mir aussuchen, und weil ich Referenzen schöner finde ...
Klar, wenn Referenzen reichen, dann nimmt man auch Referenzen.
Dobi schrieb:
Oh, richtig, gutes Argument. An Optionalität hatte ich gar nicht gedacht, weil mich die Nullprüfungen immer so sehr nerven.
WTF? Worüber reden wir denn die ganze Zeit? oO
Wenn das Objekt sicher ist, nimmt man natürlich Referenzen. Aber eben immernoch keine Smartpointer, solange der Besitz nicht übergeben wird.Dobi schrieb:
Bei SetTarget(SpaceShip* target) fühl ich mich aber schon irgendwie wieder unwohl, weil vielleicht erst später drauf geschossen wird, irgendwer anders das Ding aber schon vorher zerbombt (incl dtor-Aufruf) hat.
Und hier sprichst du den einzig sinnvollen Teil der Diskussion an. Was macht man bei dem Übergeben an Objekte, die den Pointer zwar speichern, aber das Objekt nicht besitzen. Nun, zuerst mal muss ich sagen, dass man wohl versuchen sollte, genau so eine Situation* im Voraus zu vermeiden, eben wegen genau diesem Problem. Aber wenn es wirklich nicht anders geht, wäre das die wahrscheinlich einzige Stelle, an der es Sinn macht einen shared_ptr zu wollen.
Gemeint ist eine Situation, bei der ein Objekt
1. Keine Möglichkeit hat festzustellen, ob der Pointer noch gültig ist oder nicht.
2. Nicht einfach fordern kann, dass der Pointer während der gesamten Lebenszeit erhalten bleibt.Und der wirklich interessante Fall ist, wenn 1. aber nicht 2. zutrifft. Eigentlich braucht man keinen shared_ptr - aber wie sagt man dem Aufrufer, dass man den Zeiger speichert? Das ist halt ein Problem.
-
cooky451 schrieb:
Dobi schrieb:
Klar, ich selbst weiß, dass nakte Zeiger bei mir keinen Besitz übergeben können. Meine Kollegen jedoch vielleicht nicht.
Natürlich, jeder weiß das.
In einer idealen Welt schon. Ich erleb aber oft genug, dass meine Welt keine solche ist.
cooky451 schrieb:
Und hier sprichst du den einzig sinnvollen Teil der Diskussion an. Was macht man bei dem Übergeben an Objekte, die den Pointer zwar speichern, aber das Objekt nicht besitzen. Nun, zuerst mal muss ich sagen, dass man wohl versuchen sollte, genau so eine Situation im Voraus zu vermeiden, eben wegen genau diesem Problem. Aber wenn es wirklich nicht anders geht, wäre das die wahrscheinlich einzige Stelle, an der es Sinn macht einen shared_ptr zu wollen.
Cool, dann sind wir uns ja eigentlich doch gar nicht so uneinig. Ich bin vielleicht auch nur so paraniod, weil ich schon so viele so sehr abstruse Bugs irgendwo rausoperieren durfte, und daher vorgeschädigt bin.
-
justchris schrieb:
in C++ gibt es ja mit Referenzen, Boost, STL viele Möglichkeiten nicht direkt mit Pointern in Berührung zu kommen. Nun zu meiner Frage, benutzt ihr noch viel die nackten Pointer in euren Programmen oder nutzt ihr doch mehr die Alternativen?
Compiler sind ab und zu echt nervig, wenn sie nicht in der Lage sind, aus einem indexzugriff herauszulesen, dass ich in Wahrheit nur immer wieder einen Zeiger inkrementiere. Das kann zu lustigen Ergebnissen führen, wie das der Compiler einfach mal Faktor3 langsameren Code erzeugt. Genau dann benutze ich Zeiger regelmäßig. Ansonsten nur, wenn ich Objekte ineinander schachtel: A kennt B, besitzt B aber nicht - und es ist garantiert, dass B länger lebt als A. Normalerweise würde ich hier eine Referenz verwenden wollen, aber dann kann ich A nicht mehr kopieren. Auch wieder blöd(ja in meinen Fällen reicht da immer eine shallow-copy).
-
otze schrieb:
Ansonsten nur, wenn ich Objekte ineinander schachtel: A kennt B, besitzt B aber nicht - und es ist garantiert, dass B länger lebt als A. Normalerweise würde ich hier eine Referenz verwenden wollen, aber dann kann ich A nicht mehr kopieren. Auch wieder blöd(ja in meinen Fällen reicht da immer eine shallow-copy).
struct B {}; struct A{ A( B& b ) : b_( b ) {} B& b_; }; int main() { B b; A a1( b ); A a2( a1 ); }
Oder was meinst du?
-
@otze:
Dafür gibt es ja jetzt Move-Konstruktoren. Blöd, wenn man die nicht verwenden darf.
-
struct B {}; struct A{ A( B& b ) : b_( b ) {} B& b_; }; int main() { std::vector<A> vec; }
-
Ah, ok. Da müsste man dann wohl noch sowas machen:
A& operator= (const A& rhs ) { b_ = rhs.b_; }