Dadas las diversas ediciones, tengo la impresión de que sería útil un resumen completo.
1. Cuándo no
Hay dos situaciones en las que no debería utilizar punteros inteligentes.
La primera es exactamente la misma situación en la que no debería usar una C++clase de hecho. IE: límite de DLL si no ofrece el código fuente al cliente. Digamos anecdótico.
El segundo ocurre con mucha más frecuencia: un administrador inteligente significa propiedad . Puede usar punteros para apuntar a recursos existentes sin administrar su vida útil, por ejemplo:
void notowner(const std::string& name)
{
Class* pointer(0);
if (name == "cat")
pointer = getCat();
else if (name == "dog")
pointer = getDog();
if (pointer) doSomething(*pointer);
}
Este ejemplo está limitado. Pero un puntero es semánticamente diferente de una referencia en que puede apuntar a una ubicación no válida (el puntero nulo). En este caso, está perfectamente bien no utilizar un puntero inteligente en su lugar, porque no desea administrar la vida útil del objeto.
2. Gerentes inteligentes
A menos que esté escribiendo una clase de administrador inteligente, si usa la palabra clave delete , está haciendo algo mal.
Es un punto de vista controvertido, pero después de haber revisado tantos ejemplos de código defectuoso, ya no corro riesgos. Entonces, si escribe new, necesita un administrador inteligente para la memoria recién asignada. Y lo necesitas ahora mismo.
¡No significa que seas menos programador! Por el contrario, reutilizar el código que se ha demostrado que funciona en lugar de reinventar la rueda una y otra vez es una habilidad clave.
Ahora comienza la verdadera dificultad: ¿qué administrador inteligente?
3. Punteros inteligentes
Hay varios indicadores inteligentes, con varias características.
Omitir std::auto_ptrlo que generalmente debe evitar (su semántica de copia está arruinada).
scoped_ptr: sin gastos generales, no se puede copiar ni mover.
unique_ptr: sin gastos generales, no se puede copiar, se puede mover.
shared_ptr/ weak_ptr: algunos gastos generales (recuento de referencias), se pueden copiar.
Por lo general, intente utilizar scoped_ptro unique_ptr. Si necesita varios propietarios, intente cambiar el diseño. Si no puede cambiar el diseño y realmente necesita varios propietarios, use a shared_ptr, pero tenga cuidado con los ciclos de referencias que deberían romperse con un weak_ptren algún lugar intermedio.
4. Contenedores inteligentes
Muchos punteros inteligentes no están destinados a ser copiados, por lo que su uso con los contenedores STL está algo comprometido.
En lugar de recurrir a shared_ptry sus gastos generales, use contenedores inteligentes del Boost Pointer Container . Emulan la interfaz de los contenedores STL clásicos, pero almacenan los punteros que poseen.
5. Rodando el tuyo
Hay situaciones en las que es posible que desee lanzar su propio administrador inteligente. Verifique que no se haya perdido alguna característica en las bibliotecas que está utilizando de antemano.
Escribir un administrador inteligente en presencia de excepciones es bastante difícil. Por lo general, no puede asumir que la memoria está disponible ( newpuede fallar) o que Copy Constructortiene la no throwgarantía.
Puede ser aceptable, de alguna manera, ignorar la std::bad_allocexcepción e imponer que los Copy Constructors de varios ayudantes no fallan ... después de todo, eso es lo que boost::shared_ptrhace con su Dparámetro de plantilla de eliminación .
Pero no lo recomendaría, especialmente para un principiante. Es un problema complicado y no es probable que note los errores en este momento.
6. Ejemplos
using namespace boost;
struct Cloneable
{
virtual ~Cloneable() {}
virtual Cloneable* clone() const = 0;
};
struct Derived: Cloneable
{
virtual Derived* clone() const { new Derived(*this); }
};
void scoped()
{
scoped_ptr<Cloneable> c(new Derived);
}
unique_ptr<Cloneable> unique()
{
return unique_ptr<Cloneable>(new Derived);
}
void shared()
{
shared_ptr<Cloneable> n1(new Derived);
weak_ptr<Cloneable> w = n1;
{
shared_ptr<Cloneable> n2 = n1;
n1.reset();
assert(n1.get() == 0);
assert(n2.get() != 0);
assert(!w.expired() && w.get() != 0);
}
assert(w.expired());
}
void container()
{
ptr_vector<Cloneable> vec;
vec.push_back(new Derived);
vec.push_back(new Derived);
vec.push_back(
vec.front().clone()
);
}