Zeiger als Referenz
-
Moin moin,
ich hab ne kleine Stilfrage, die ich an dieser Stelle gerne mal erötern möchte. Sei folgender Code gegeben:
template<typename T> class foo{ private: std::unique_ptr<T> ptr; public: foo() : ptr(new T){} };In meinem Code kommen solche Konstrukte öfter mal als Wrapper-Klassen vor. Das Problem hierbei ist, dass die Aufspaltung der zu "wrappenden" Logik in andere Sinneinheiten von einer solchen Klasse meist einen Zugriff auf den internen Zeiger - im Sinne einer Referenz - verlangt. Diesen Zugriff realisiere ich meist, in dem ich z.B. nen Pointer oder Referenz über ne Methode zurückgebe.
Nun sind aber meist folgende Fälle abzuwägen:
Nehme ich doch lieber nen shared_pointer als Member und gebe auch einen über die Methode zurück? Find ich hier Fall "unschön", weil es geteilten Besitz der Ressource anzeigen würde, was aber nicht der Fall ist.
Eine Referenz kann ich nicht immer nehmen, da die Bindung des obigen Zeigers in Instanzen anderer Klassen öfters mal dauerhafter Natur sein soll(also in Form eines Members). Und es kommt nicht selten vor, dass ich auch erst nach Konstruktion des Objektes binden möchte.
Ein roher Zeiger birgt wiederum die Gefahr, dass er "deleted" wird.Meine Fragen:
Wie vermeidet/löst ihr ein solches Konstrukt? Schreibt ihr euch immer ne kleine Wrapper-Klasse für den rohen Zeiger und gebt diese zurück?
Und zweitens: Die Verwendung eines Zeigers als spät-bindende Referenz kommt ja nicht allzu selten vor. Warum gibt es in der StdLib kein entsprechendes Konstrukt(ref_ptr?), welches eine solche Semantik standardisiert?
Bzw. was hab ich übersehen? :p
-
Matzer schrieb:
Nehme ich doch lieber nen shared_pointer als Member und gebe auch einen über die Methode zurück? Find ich hier Fall "unschön", weil es geteilten Besitz der Ressource anzeigen würde, was aber nicht der Fall ist.
Genau, würde ich aus diesem Grund nicht tun.
shared_ptrsollte man wirklich nur bei geteilten Dingen verwenden, leider wird er viel zu oft missbraucht.Matzer schrieb:
Eine Referenz kann ich nicht immer nehmen, da die Bindung des obigen Zeigers in Instanzen anderer Klassen öfters mal dauerhafter Natur sein soll(also in Form eines Members). Und es kommt nicht selten vor, dass ich auch erst nach Konstruktion des Objektes binden möchte.
Das verstehe ich nicht. Was verstehst du unter "Binden"? Also doch geteilten Besitz?
Matzer schrieb:
Ein roher Zeiger birgt wiederum die Gefahr, dass er "deleted" wird.
Aus diesem Grund keine Zeiger zu verwenden finde ich etwas fragwürdig. Kein vernünftiger Mensch wendet
deleteauf irgendeinen Zeiger an, von dem er nichts über Speicherverwaltung weiss.Matzer schrieb:
Wie vermeidet/löst ihr ein solches Konstrukt? Schreibt ihr euch immer ne kleine Wrapper-Klasse für den rohen Zeiger und gebt diese zurück?
Nein, Wrapper nur, wenn es sich beim Zeiger um ein Implementierungsdetail handelt oder man dadurch elegantere Handhabung erreicht (z.B. Smart-Pointer). Aber einen Wrapper, nur damit man einen Wrapper hat... Eher nicht.
Aber könntest du vielleicht ein konkretes Beispiel bringen?
Matzer schrieb:
Und zweitens: Die Verwendung eines Zeigers als spät-bindende Referenz kommt ja nicht allzu selten vor. Warum gibt es in der StdLib kein entsprechendes Konstrukt(ref_ptr?), welches eine solche Semantik standardisiert?
Es gibt ja bereits Zeiger. Oder was meinst du mit spät-bindenden Referenzen, dass diese eine eigene Klasse verdient hätten?
-
Das verstehe ich nicht. Was verstehst du unter "Binden"? Also doch geteilten Besitz?
Angenommen ich habe
class bar{ private: int & ref; void bind_late(int& i){ ref = i; //Geht nicht } };Nehme ich Referenzen, kann die Bindung der Referenz immer nur im Konstruktor erfolgen. Das ist nicht immer erwünscht...
Zudem ist in diesem Fall ref kein Besitzer, sondern er referenziert irgendwas, was auch nach Konstruktion des Objekts geändert werden kann(wofür nur Pointer in Frage kommen).
Es kommt nun an dieser Stelle darauf an, ob ich die Bedeutung des Begriffs "Besitztum" richtig verstanden hab. Oders anders gefragt: Wäre an dieser Stelle ein shared_ptr anstelle der Referenz angebracht?Aus diesem Grund keine Zeiger zu verwenden finde ich etwas fragwürdig. Kein vernünftiger Mensch wendet delete auf irgendeinen Zeiger an, von dem er nichts über Speicherverwaltung weiss.
Ich verwende sehr wohl rohe Zeiger, obwohl ich des öfteren Bedenken habe, mein Code könne "zweckentfremdet" werden
Es geht mir mehr darum, die Bedeutung des Codes deutlicher zu machen. Gibt mir eine Methode nen rohen Pointer zurück, weiß ich auf Anhieb nicht: Darf ich deleten, darf ich nicht? Wer regelt die Speicherverwaltung? Ich kann zwar in der Doku nachschauen, aber Code sollte sich - meiner Meinung nach - in gewisser Weise selbst erklären.Nein, Wrapper nur, wenn es sich beim Zeiger um ein Implementierungsdetail handelt oder man dadurch elegantere Handhabung erreicht (z.B. Smart-Pointer). Aber einen Wrapper, nur damit man einen Wrapper hat... Eher nicht.
Aber könntest du vielleicht ein konkretes Beispiel bringen?
Ich versuche es lieber nochmal zu verdeutlichen:
Wir haben in der StdLib zwei Klassen(unique_ptr und shared_ptr), die auf semantischer Ebene einen Besitz anzeigen. Wieso auch nicht den letzten Fall mittels einer Klasse modellieren, in dem ein Pointer eine simple Referenz darstellt (und der nicht "deleted" werden kann)? Wem alles egal ist, der kann ja weiterhin rohe Zeiger verwenden :p
-
Matzer schrieb:
Wäre an dieser Stelle ein shared_ptr anstelle der Referenz angebracht?
Im Normalfall nicht. Wenn das Objekt ausserhalb der Klasse gespeichert wird, und du in der Klasse lediglich einen Verweis haben willst, nimm eine Referenz oder einen Zeiger. Was du von den beiden nimmst, ist ohnehin ein Implementierungsdetail.
Matzer schrieb:
Es geht mir mehr darum, die Bedeutung des Codes deutlicher zu machen. Gibt mir eine Methode nen rohen Pointer zurück, weiß ich auf Anhieb nicht: Darf ich deleten, darf ich nicht? Wer regelt die Speicherverwaltung? Ich kann zwar in der Doku nachschauen, aber Code sollte sich - meiner Meinung nach - in gewisser Weise selbst erklären.
Dann mach es doch einfach so, dass du (nahezu) nie dynamisch angeforderten Speicher über rohe Zeiger zurückgibst. Dazu empfiehlt sich
std::auto_ptroder in C++0xstd::unique_ptr, damit hast du nicht wirklich Overhead und die Semantik ist klar.Oder beschränke die Rückgabe besitzender roher Zeiger z.B. auf Factory-Funktionen, die mit "Create" beginnen.
Matzer schrieb:
Wir haben in der StdLib zwei Klassen(unique_ptr und shared_ptr), die auf semantischer Ebene einen Besitz anzeigen. Wieso auch nicht den letzten Fall mittels einer Klasse modellieren, in dem ein Pointer eine simple Referenz darstellt (und der nicht "deleted" werden kann)? Wem alles egal ist, der kann ja weiterhin rohe Zeiger verwenden :p
Wahrscheinlich weil ein passiver Zeiger in den meisten Fällen der Normalfall ist und eine spezialisierte Klasse nicht wirklich viele Vorteile brächte, aber einiges komplexer machen würde. Ich sehe wie schon angetönt mehr Potenzial darin, rohe besitzende Zeiger auf ein Minimum zu beschränken und spezielle Besitzsemantiken mittels Smart-Pointern zu verdeutlichen. So kannst du rohe Zeiger und Referenzen primär für passive Verweise einsetzen. Damit erledigt sich auch das "aus Versehen
delete"-Problem.Viel schlimmer als das Fehlen einer solchen Klasse finde ich zum Beispiel, dass ich bisher kaum eine vernünftige Smart-Pointer-Klasse mit Kopiersemantik gesehen habe, obwohl man das meiner Meinung nach doch ab und zu braucht. Viele Leute führen sowas dann wieder manuell durch und implementieren überaus oft die Grossen Drei. Und Boost empfiehlt für das Pimpl-Idiom im vollen Ernst
shared_ptr...