Einfacher shared_ptr-Ersatz?
-
Hi,
ich habe hier fertigen Code, der auch auf einem Embedded-Board laufen soll. Die Portierung ist fast fertig, ich hänge nur an einem letzten Problem: ich benötige einen einfachen Ersatz für einen shared_ptr. Boost kann ich nicht verwenden, da der Compiler für dieses Board ziemlich antik ist.
Also benötige ich eine eigene Implementierung. Templates sind nicht notwendig, da dieser eigene shared_ptr mit genau einem Datentyp funktionieren soll.
Hat jemand einen Tipp, wo man so eine _getestete_ Ersatzimplementierung finden kann?
!Zurg
-
Du schreibst dir eine Klasse, die als Datenmember einen size_t count; und einen typ *data; hat.
Im Konstruktor wird Speicher angefordert und count erhöht. Im Destruktor wird count erniedrigt und wenn count 0 ist, der Speicher freigegeben. Im Copy-Konstruktor/Zuweisung wird count erhöht und kopiert. Flache Kopie beim Pointer!
Dann schreibst du dir noch die getter/setter, die du brauchst.
-
Über wie antik reden wir? Kennt er überhaupt Templates?
Es gibt BCP zum Extrahieren einzelner Codeteile von Boost, wenn dir die gesamte Bibliothek zu groß ist. Ansonsten sehe ich eher schwarz für dich, da die Anforderungen doch schon etwas speziell sind. Andererseits, auch wenn du diese Antwort nicht haben willst, ist ein shared_ptr für genau eine Klasse kein Hexenwerk, wenn du nur einen Referenzzähler und einen operator* anzubieten brauchst. Schwieriger wirds erst bei Multithreading und das brauchst du nicht, oder?
-
Ich würde einen selber schreiben. "Echte"
shared_ptrsind auf Multithreading ausgelegt und deswegen langsam. Wenn du weisst, dass alles in einem Thread passiert, kannst du ruhig eine naive Implementierung selber vornehmen.weak_ptrbrauchst du vermutlich auch nicht.
-
Michael E. schrieb:
Über wie antik reden wir? Kennt er überhaupt Templates?
Im Prinzip ja, aber da würde ich auf keine vollständig funktionierende Implementierung hoffen. Aber das ist ja auch kein Problem, da mein shared_ptr ja auch nur mit genau einem Datentypen umgehen können muss.
-
Zurg schrieb:
Michael E. schrieb:
Über wie antik reden wir? Kennt er überhaupt Templates?
Im Prinzip ja, aber da würde ich auf keine vollständig funktionierende Implementierung hoffen. Aber das ist ja auch kein Problem, da mein shared_ptr ja auch nur mit genau einem Datentypen umgehen können muss.
Naja, ein Problem ist es insofern, dass praktisch jede Implementierung Templates benutzen dürfte, d.h. du darfst sowieso nochmal Hand anlegen. Dann würde ich wirklich selbst implementieren und drei Tests dazu schreiben.
-
Nathan schrieb:
Du schreibst dir eine Klasse, die als Datenmember einen size_t count; und einen typ *data; hat.
Im Konstruktor wird Speicher angefordert und count erhöht. Im Destruktor wird count erniedrigt und wenn count 0 ist, der Speicher freigegeben. Im Copy-Konstruktor/Zuweisung wird count erhöht und kopiert. Flache Kopie beim Pointer!
Dann schreibst du dir noch die getter/setter, die du brauchst.So schwierig ist der nun auch wieder nicht...
-
OK, prinzipiell funktioniert es. Aber: Bei Shared Pointer Objekten kann ich mit
if (mySharedPtr) ...auf Gültigkeit des gekapselten Pointers überprüfen. Wie implementiere ich das denn?
-
du musst operator (bool) implementieren.
-
Maxi schrieb:
du musst operator (bool) implementieren.
Bei einem alten Compiler dann doch lieber das "Safe-Bool-Idiom" verwenden (siehe z.B. hier)
Naiv den operator bool zu überladen kann unerwartete Ergebnisse hervorbringen

In C++11 wurde das durch explicit gefixt (siehe Wikipedia)
-
Nathan schrieb:
Du schreibst dir eine Klasse, die als Datenmember einen size_t count; und einen typ *data; hat.
Im Konstruktor wird Speicher angefordert und count erhöht. Im Destruktor wird count erniedrigt und wenn count 0 ist, der Speicher freigegeben. Im Copy-Konstruktor/Zuweisung wird count erhöht und kopiert. Flache Kopie beim Pointer!
Dann schreibst du dir noch die getter/setter, die du brauchst.Das ist aber falsch. Wenn Du eine Kopie des shared pointers machst, dann muss die Kopie auf den selben Zähler zeigen. So hast Du eine Kopie des Zählers. Beide shares pointer haben einen Zählerstand von 2 und wenn der eine gelöscht wird, dekrementiert er seinen auf 1 und löscht das Objekt nicht und wenn der andere gelöscht wird, dekrementiert er eben seinen auf 1 und löscht das Objekt ebenfalls nicht.
Du musst beim Erzeugen eines shared pointers einen Zähler separat alloziieren und darauf verweisen. Beim kopieren verweisen dann beide auf den selben Zähler. Wird der Zähler bei löschen des shared pointers 0, muss der shared pointer sowohl das Objekt als auch den Zähler löschen.
Es gibt verschiedene Varianten, wie man auf die zusätzliche Allokation verzichten kann, aber das macht die Sache meist komplizierter. Im einfachsten Fall kannst Du allerdings das Objekt, welches referenziert wird, um einem Referenzzähler erweitern. Dann wird das sogar noch robuster.
-
Oh stimmt ja.
Beide brauchen den gleichen Zähler.
Sry, mein Fehler.
-
tntnet schrieb:
Es gibt verschiedene Varianten, wie man auf die zusätzliche Allokation verzichten kann, aber das macht die Sache meist komplizierter.
Das Problem tritt ja bei
std::shared_ptr<T> ptr(new T(args));auf, wo wir für das Objekt und für den Reference Counter zwei Mal
newbrauchen. Darum gibts auch das sehr eleganteauto ptr = std::make_shared<T>(args);Wenn du deinen Shared-Pointer auf genau einen Typen auslegst, musst du den Konstruktor mit einem
T*gar nicht anbieten, sondern kannst direkt dessen Konstruktorargumente übergeben. Damit hast du wiederum die Möglichkeit, das Objekt intern zu erstellen, und zwar gleich zusammen mit dem Reference Counter.
-
Zurg schrieb:
Hat jemand einen Tipp, wo man so eine _getestete_ Ersatzimplementierung finden kann?
Hallo Zurg,
ich hatte vor Jahren mal so was gebaut ... das sollte auch funktionieren:
template< typename T > class shared_ptr { public: explicit shared_ptr( T* p = 0 ) : m_ptr( p ) , m_ref_count( new int(1) ) {} shared_ptr( const shared_ptr& b ) : m_ptr( b.m_ptr ) , m_ref_count( b.m_ref_count ) { ++*m_ref_count; } ~shared_ptr() { if( --*m_ref_count == 0 ) { delete m_ref_count; delete m_ptr; } } shared_ptr& operator=( const shared_ptr& b ) { shared_ptr tmp( b ); swap( tmp ); return *this; } void swap( shared_ptr& b ) { std::swap( m_ptr, b.m_ptr ); std::swap( m_ref_count, b.m_ref_count ); } void reset( T* p = 0 ) { shared_ptr tmp( p ); swap( tmp ); } // -- Pointer Access T* operator->() const { assert( m_ptr ); return m_ptr; } T& operator*() const { assert( m_ptr ); return *m_ptr; } T* get() const { return m_ptr; } operator const void*() const { return m_ptr? this: 0; } bool operator!() const { return m_ptr == 0; } private: T* m_ptr; int* m_ref_count; }; template< typename T > void swap( shared_ptr< T >& a, shared_ptr< T >& b ) { a.swap( b ); }benötigt includes <cassert> und <algorithm> - für std::swap.
Gruß
Werner