Klasse die sich selbst als Pointer sieht
-
Ich würde die get-Funktion als freie friend-Funktion implementieren. In Modern C++ Design wird das gut erklärt.
Wenn das Objekt eine Methode get() hat, ist der Unterschied zwischen ptr->get() und ptr.get() ziemlich gering und führt zu möglichen Fehlern.
Bei get(ptr) ist das nicht möglich.
-
314159265358979 schrieb:
Ich würde die get-Funktion als freie friend-Funktion implementieren. In Modern C++ Design wird das gut erklärt.
Wenn das Objekt eine Methode get() hat, ist der Unterschied zwischen ptr->get() und ptr.get() ziemlich gering und führt zu möglichen Fehlern.
Bei get(ptr) ist das nicht möglich.Danke für den Tipp, ich habs wie folgt probiert:
template<class T> class smartpointer; template<class T> T* getptr(smartpointer<T>& obj) { // Warnmeldung für den Programmierer ausgeben #define __STR2__(x) #x #define __STR1__(x) __STR2__(x) #define __LOC__ __FILE__ "("__STR1__(__LINE__)") : warning : T* smartpointer<T>::getptr() : " #pragma message(__LOC__"Using this may be very unsafe, pls ensure right handling and DON'T use if not neccessary.") assert(obj.zeigeraufobject); return obj.zeigeraufobject; } template<class T> class smartpointer { public: // [...] friend T* getptr(smartpointer<T>& obj); // [...] };
**
Problem ist das der Linker einen Fehler (nicht aufgelöster externer Verweis) ausspukt, wenn ich smartpointer für mehr als eine Klasse verwende. Was hab ich falsch gemacht?
**MfG
Scarabol
-
Passt zwar nicht so zu deiner Fehlerbeschreibung, dürfte aber trotzdem ein Problem sein: benutze das inline Keyword um die ODR nicht zu verletzen.
Edit:
Und zu deiner Fehlermeldung:
Tue die freie Funktion getptr(..) Vorwärtsdekl., dann die Klasse smartpointer definieren und dann die freie Funktion getptr(..) definieren.
-
Meinst du so:
template<class T> T* getptr(smartpointer<T>& obj); template<class T> class smartpointer { public:
Dann ist smartpointer<T> ja nicht definiert und bei:
template<class T> class smartpointer; template<class T> T* getptr(smartpointer<T>& obj); template<class T> class smartpointer { public:
Erhalte ich die gleiche Fehlermeldung wie oben.
MfG
Scarabol
-
Scarabol schrieb:
Ich muss den smartpointer<T> Zeiger an eine externe DLL weiterleiten, daher der implizite cast.
Überall wo du
operator T*
einsetzen würdest, kannst duGet()
verwenden – nur halt explizit.Ich kann mich krümelkacker nur anschliessen: Vermeide implizite Konvertierungen, denn nur so hast du die volle Kontrolle über den Besitz. Mach den Konstruktor
explicit
. Was für eine Besitzsemantik hat der Zeiger überhaupt?314159265358979 schrieb:
Ich würde die get-Funktion als freie friend-Funktion implementieren. In Modern C++ Design wird das gut erklärt.
Wenn das Objekt eine Methode get() hat, ist der Unterschied zwischen ptr->get() und ptr.get() ziemlich gering und führt zu möglichen Fehlern.
Bei get(ptr) ist das nicht möglich.Die Argumentation hat mich in Modern C++ Design nie so richtig überzeugt. Einerseits kann man genausogut aus Versehen
get(*ptr)
schreiben, andererseits wird der Unterschied mit aussagekräftigeren Namen als "get" auch grösser. Und drittens sind Smart-Pointer spätestens seitshared_ptr
einigermassen bekannt, und auch ihr Klassen-Status ist nicht überraschend. Von daher würde ich eher zu diesen konsistent sein. Ich sehe den riesigen Vorteil von globalen Funktionen hier nicht.
-
Hier ist die komplette Klasse als Headerdatei:
smartpointer.h#ifndef inc_smartpointer_h #define inc_smartpointer_h #include <cassert> template<class T> class smartpointer { public: smartpointer(T *p = 0); ~smartpointer(); T* operator->() const; T& operator*() const; // getptr gibt direkt den internen Zeiger auf das T-Objekt heraus. Also vorsicht und bitte nur im äußersten Notfall einsetzen T* getptr(void) const; smartpointer& operator=(T *p); operator bool() const; void loescheobject(); private: void operator=(const smartpointer&); smartpointer(const smartpointer&); T* zeigeraufobject; }; template<class T> inline smartpointer<T>::smartpointer(T *p) : zeigeraufobject(p) { } template<class T> inline smartpointer<T>::~smartpointer() { loescheobject(); } template<class T> inline T* smartpointer<T>::operator->() const { assert(zeigeraufobject); return zeigeraufobject; } template<class T> inline T& smartpointer<T>::operator*() const { assert(zeigeraufobject); return *zeigeraufobject; } template<class T> inline T* smartpointer<T>::getptr(void) const { // Warnmeldung für den Programmierer ausgeben #define __STR2__(x) #x #define __STR1__(x) __STR2__(x) #define __LOC__ __FILE__ "("__STR1__(__LINE__)") : warning : T* smartpointer<T>::getptr() : " #pragma message(__LOC__"Using this may be very unsafe, pls ensure right handling and DON'T use if not neccessary.") assert(zeigeraufobject); return zeigeraufobject; } template<class T> inline smartpointer<T>& smartpointer<T>::operator=(T* p) { assert(zeigeraufobject == 0); zeigeraufobject = p; return *this; } template<class T> inline smartpointer<T>::operator bool() const { return bool(zeigeraufobject); } template<class T> inline void smartpointer<T>::loescheobject() { delete zeigeraufobject; zeigeraufobject = 0; } #endif
MfG
Scarabol
-
Nexus schrieb:
Die Argumentation hat mich in Modern C++ Design nie so richtig überzeugt. Einerseits kann man genausogut aus Versehen
get(*ptr)
schreiben, andererseits wird der Unterschied mit aussagekräftigeren Namen als "get" auch grösser. Und drittens sind Smart-Pointer spätestens seitshared_ptr
einigermassen bekannt, und auch ihr Klassen-Status ist nicht überraschend. Von daher würde ich eher zu diesen konsistent sein. Ich sehe den riesigen Vorteil von globalen Funktionen hier nicht.Angenommen, das Objekt hat eine Methode get() die auch einen T* zurückgibt. Ein Tippfehler würde reichen, um etwas völlig anderes zu machen. Klar, dieser Fall wird sicher nicht oft vorkommen, aber möglich ist es.
-
Wie gesagt: Da gibt es noch einiges zu tun, um eine robuste Semantik zu gewährleisten.
- Konstruktor
explicit
- Zuweisungsoperator von rohem Zeiger entfernen
operator bool
durch Safe-Bool-Idiom ersetzen, da sonst Dinge wiea == b
mit unerwartetem Ergebnis möglich sindgetptr()
darf eigentlich Nullzeiger zurückgeben, daher keinassert
verwenden
Hast du kein Boost? Sonst könntest du
boost::scoped_ptr
verwenden, der ist schon voll optimiert und getestet. Ist auch eine Header-Only-Datei, du müsstest also nichts linken.
- Konstruktor
-
314159265358979 schrieb:
Angenommen, das Objekt hat eine Methode get() die auch einen T* zurückgibt. Ein Tippfehler würde reichen, um etwas völlig anderes zu machen. Klar, dieser Fall wird sicher nicht oft vorkommen, aber möglich ist es.
Ja, ich habe deine Aussage schon verstanden. Aber wie gesagt: Ein Tippfehler kann auch zu einem zusätzlichen
*
führen. Und generell kommt mir das ein bisschen vor wieif (a == 4) // man muss hier if (4 == a) // schreiben, weil der allgegenwärtige // Fehler a = 4 so verhindert wird!
-
Das mit dem zusätzlichen * wird wohl nicht funktionieren, da die Funktion einen smart_ptr<T> nimmt und dessen Konstruktor i.d.R. explicit ist
-
Vereinfacht:
X* Get(SmartPtr<X> p); X* Get(X x); SmartPtr<X> ptr; Get(*p); // !
Aber das war ohnehin nur eine Bemerkung am Rande. Das kommt derart selten vor, da lohnt es sich nicht stundenlang darüber zu diskutieren. Bei einem besseren Namen als "Get" kommt die Situation erst recht nie vor. Ich habe ja noch andere Punkte genannt und wollte primär aussagen, dass ich die Variante mit der freien Funktion nicht für so viel besser halte wie hier dargestellt. Und wenn ich sie verfechtete, würde ich andere Argumente in den Vordergrund rücken (z.B. Generizität).
-
Nexus schrieb:
Wie gesagt: Da gibt es noch einiges zu tun, um eine robuste Semantik zu gewährleisten.
- Konstruktor
explicit
- Zuweisungsoperator von rohem Zeiger entfernen
operator bool
durch Safe-Bool-Idiom ersetzen, da sonst Dinge wiea == b
mit unerwartetem Ergebnis möglich sindgetptr()
darf eigentlich Nullzeiger zurückgeben, daher keinassert
verwenden
Versteh ich soweit alles aber was hat es mit dem safe bool auf sich und wieso darf ich den Zuweisungsoperator für rohe Zeiger nicht benutzen?
MfG
Scarabol
- Konstruktor
-
Google wird dir weiterhelfen.
-
Schade
-
Dein Code erlaubt folgendes:
if(ptr + 42) { play_sound("booom.wav"); message_box("buffer overflow"); return 666; }
Dafür kanst du das Safe-Bool Idiom anwenden:
class smartpointer { //... private: void some_mem_func(){} typedef void(smartpointer::* bool_t)(); public: operator bool_t () { return ptr ? &smartpointer::some_mem_func : 0; } //... };
-
Achso cool danke.
MfG
Scarabol
-
Das Rad, was du jetzt erfindest, wird ecking sein. Wenn Du es hinsichtlich
Flexibilität und Benutzungsfehleranfälligkeit "abrundest", wird dann so etwas wie boost::scoped_ptr oder std::unique_ptr rauskommen. Die haben all die Kinderkrankheiten nicht mehr. Letzteren smart pointer gibt's offiziell leider noch nicht, kommt aber in der nächsten C++ Version.
-
Bei scoped_ptr macht mich folgender Satz stutzig:
T * operator->() const; // never throws
Returns the stored pointer. Behavior is undefined if the stored pointer is 0.Soll also heißen, dass die Klasse auch nicht so perfekt ist oder?
MfG
Scarabol
-
Scarabol schrieb:
Soll also heißen, dass die Klasse auch nicht so perfekt ist oder?
Wie? In welchem Kontext soll die Dereferenzierung eines Nullzeigers erlaubt sein?
-
Es soll ja nicht erlaubt sein, aber ein assert währe doch sehr wünschenswert finde ich.
MfG
Scarabol