Sugiero ir con el puntero inteligente intrusivo Boost.
También hay una aplicación de Scott Meyer (aquí: http://www.aristeia.com/BookErrata/M29Source.html) que se publica en 'More Effective C++'
Sin embargo, en caso de que ayuda, me tiró un simple puntero refcounting (con
algún apoyo para las asignaciones polimórficos
y deletors personalizados). Este se decidió desentierrar-aware.
Nota: he olvidado eso. Las asignaciones polimórficas estaban en una variación para otro proyecto. Eso también lo tengo, pero no es compatible con el eliminador personalizado :) Avíseme si alguien está interesado; Por supuesto se trata con pruebas unitarias separadas para la función
Viene con pruebas unitarias (por ejemplo, para comprobar si hay el famoso bug remove linked list node
pedido).Así que ya saben lo que se obtiene :)
/*
* counted_ptr - simple reference counted pointer.
*
* The is a non-intrusive implementation that allocates an additional
* int and pointer for every counted object.
*/
#ifndef COUNTED_PTR_H
#define COUNTED_PTR_H
#include <stdlib.h>
extern "C" bool mtx_unit_test_countedptr();
namespace MtxChess {
/* For ANSI-challenged compilers, you may want to #define
* NO_MEMBER_TEMPLATES or explicit */
template <class X>
struct FreeMallocPolicy
{
static void do_free(X* p) { if (p) ::free(p); p = 0; }
};
template <class X>
struct ScalarDeletePolicy
{
static void do_free(X* p) { if (p) delete p; p = 0; }
};
template <class X>
struct ArrayDeletePolicy
{
static void do_free(X* p) { if (p) delete[] p; p = 0; }
};
template <class X,class _P=ScalarDeletePolicy<X> > class counted_ptr
{
public:
typedef X element_type;
explicit counted_ptr(X* p = 0) // allocate a new counter
: itsCounter(0) {if (p) itsCounter = new counter(p);}
~counted_ptr()
{release();}
counted_ptr(const counted_ptr& r) throw()
{acquire(r.itsCounter);}
operator bool() const { return 0!=get(); }
void clear() { (*this) = counted_ptr<X>(0); }
counted_ptr& operator=(const counted_ptr& r)
{
if (this != &r) {
auto_release keep(itsCounter);
acquire(r.itsCounter);
}
return *this;
}
bool operator<(const counted_ptr& r) const
{
return get()<r.get();
}
bool operator==(const counted_ptr& r) const
{
return get()==r.get();
}
bool operator!=(const counted_ptr& r) const
{
return get()!=r.get();
}
#ifndef NO_MEMBER_TEMPLATES
// template <class Y> friend class counted_ptr<Y>;
template <class Y> counted_ptr(const counted_ptr<Y>& r) throw()
{acquire(r.itsCounter);}
template <class Y> counted_ptr& operator=(const counted_ptr<Y>& r)
{
if (this != &r) {
auto_release keep(itsCounter);
acquire(r.itsCounter);
}
return *this;
}
template <class Y> bool operator<(const counted_ptr<Y>& r) const
{
return get()<r.get();
}
template <class Y> bool operator==(const counted_ptr<Y>& r) const
{
return get()==r.get();
}
template <class Y> bool operator!=(const counted_ptr<Y>& r) const
{
return get()!=r.get();
}
#endif // NO_MEMBER_TEMPLATES
X& operator*() const throw() {return *itsCounter->ptr;}
X* operator->() const throw() {return itsCounter->ptr;}
X* get() const throw() {return itsCounter ? itsCounter->ptr : 0;}
bool unique() const throw()
{return (itsCounter ? itsCounter->count == 1 : true);}
private:
struct counter {
counter(X* p = 0, unsigned c = 1) : ptr(p), count(c) {}
X* ptr;
unsigned count;
}* itsCounter;
void acquire(counter* c) throw()
{
// increment the count
itsCounter = c;
if (c) ++c->count;
}
void release()
{
dorelease(itsCounter);
}
struct auto_release
{
auto_release(counter* c) : _c(c) {}
~auto_release() { dorelease(_c); }
counter* _c;
};
void static dorelease(counter* itsCounter)
{
// decrement the count, delete if it is 0
if (itsCounter) {
if (--itsCounter->count == 0) {
_P::do_free(itsCounter->ptr);
delete itsCounter;
}
itsCounter = 0;
}
}
};
} // EON
#endif // COUNTED_PTR_H
Las pruebas unitarias (compila como autónomo)
/*
* counted_ptr (cpp) - simple reference counted pointer.
*
* The is a non-intrusive implementation that allocates an additional
* int and pointer for every counted object.
*/
#include "counted_ptr.hpp"
#include "internal.hpp"
#include <map>
#include <string>
namespace MtxChess {
namespace /*anon*/
{
// sensed events
typedef std::map<std::string, int> Events;
static Events constructions, destructions;
struct Trackable
{
Trackable(const std::string& id) : _id(id) { constructions[_id]++; }
~Trackable() { destructions[_id]++; }
const std::string _id;
};
typedef counted_ptr<Trackable> target_t;
bool testBehaviour()
{
static const counted_ptr<Trackable> Nil = target_t(0);
bool ok = true;
constructions.clear();
destructions.clear();
MTXASSERT_EQ(ok, 0ul, constructions.size());
MTXASSERT_EQ(ok, 0ul, destructions.size());
target_t a = target_t(new Trackable("aap"));
MTXASSERT_EQ(ok, 1ul, constructions.size());
MTXASSERT_EQ(ok, 1, constructions["aap"]);
MTXASSERT_EQ(ok, 0ul, destructions.size());
MTXASSERT_EQ(ok, 0, constructions["noot"]);
MTXASSERT_EQ(ok, 2ul, constructions.size());
MTXASSERT_EQ(ok, 0ul, destructions.size());
target_t hold;
{
target_t b = target_t(new Trackable("noot")),
c = target_t(new Trackable("mies")),
nil = Nil,
a2 = a;
MTXASSERT(ok, a2==a);
MTXASSERT(ok, nil!=a);
MTXASSERT_EQ(ok, 3ul, constructions.size());
MTXASSERT_EQ(ok, 1, constructions["aap"]);
MTXASSERT_EQ(ok, 1, constructions["noot"]);
MTXASSERT_EQ(ok, 1, constructions["mies"]);
MTXASSERT_EQ(ok, 0, constructions["broer"]);
MTXASSERT_EQ(ok, 4ul, constructions.size());
MTXASSERT_EQ(ok, 0ul, destructions.size());
hold = b;
}
MTXASSERT_EQ(ok, 1ul, destructions.size());
MTXASSERT_EQ(ok, 0, destructions["aap"]);
MTXASSERT_EQ(ok, 0, destructions["noot"]);
MTXASSERT_EQ(ok, 1, destructions["mies"]);
MTXASSERT_EQ(ok, 3ul, destructions.size());
hold = Nil;
MTXASSERT_EQ(ok, 3ul, destructions.size());
MTXASSERT_EQ(ok, 0, destructions["aap"]);
MTXASSERT_EQ(ok, 1, destructions["noot"]);
MTXASSERT_EQ(ok, 1, destructions["mies"]);
MTXASSERT_EQ(ok, 4ul, constructions.size());
// ok, enuf for now
return ok;
}
struct Linked : Trackable
{
Linked(const std::string&t):Trackable(t){}
counted_ptr<Linked> next;
};
bool testLinked()
{
bool ok = true;
constructions.clear();
destructions.clear();
MTXASSERT_EQ(ok, 0ul, constructions.size());
MTXASSERT_EQ(ok, 0ul, destructions.size());
counted_ptr<Linked> node(new Linked("parent"));
MTXASSERT(ok, node.get());
node->next = counted_ptr<Linked>(new Linked("child"));
MTXASSERT_EQ(ok, 2ul, constructions.size());
MTXASSERT_EQ(ok, 0ul, destructions.size());
node = node->next;
MTXASSERT(ok, node.get());
MTXASSERT_EQ(ok, 2ul, constructions.size());
MTXASSERT_EQ(ok, 1ul, destructions.size());
node = node->next;
MTXASSERT(ok,!node.get());
MTXASSERT_EQ(ok, 2ul, constructions.size());
MTXASSERT_EQ(ok, 2ul, destructions.size());
return ok;
}
}
} // EON
int main()
{
using namespace MtxChess;
bool ok = true;
ok = testBehaviour() && ok;
ok = testLinked() && ok;
return ok?0:1;
}
¿es realmente tan malo el gasto general? – Anycorn
De acuerdo. La sobrecarga es tan mínima que no debería ser el cuello de botella. Si tienes un cuello de botella en alguna parte, no está en shared_ptr. – inestical
@inestical: para este caso, probablemente tengas razón, pero no olvides que shared_ptr asigna su recuento en el montón. Hacer eso un millón de veces puede causar gastos generales serios. Por ejemplo, http://stackoverflow.com/questions/3628081/shared-ptr-horrible-speed – stijn