[C con Clase] En relación a Shared_Ptr y memory leaks

Davidson, Steven srd4121 en njit.edu
Dom Dic 29 18:30:13 CET 2013


Hola Miguel Ángel,

2013/12/29 Miguel Ángel Torres Fernández-Píñar <
miguelangeltorresfp en gmail.com>

> Buenos días a todos.
>
> Me han recomendado usar mejor punteros inteligentes ( Shared_Ptr ) para
> miembros de una clase, para evitar memory leaks, sobre todo en casos en el
> que tenga que cargar imágenes, textos, etc..
>
> Me dicen además que de esta forma puedo crear estos objetos en el momento
> en el que me convenga. Que no se tienen que crear cuando instancio un
> objeto de la clase.
>
> Me comentan que debo evitar crear objetos con el operador "new".
>
> Pero sigo sin tener todo esto muy claro.
>
>
La idea es reducir la posibilidad de cometer errores durante la
programación. El problema de usar memoria dinámicamente adjudicada es que
tienes que liberarla. Si uno no tiene cuidado, o simplemente no tiene
control acerca del momento de perder esa dirección de memoria, entonces
perdemos la oportunidad de liberar esa memoria. A esto lo llamamos "fuga de
memoria" (o en inglés, memory leak).

Existen varias clases-plantilla, que principalmente son: 'shared_ptr',
'unique_ptr' (previamente, 'auto_ptr'), y 'weak_ptr' que sirven para
representar punteros agregando algún "grado" de seguridad. Con esto,
podemos crear un sistema de gestión de memoria que posiblemente tenga una
"recogida de basura" (en inglés, garbage collection), que alivia el
problema y peligro de las fugas de memoria, automatizando la liberación de
memoria previamente adjudicada dinámicamente. Por ello, a estas
clases-plantilla se llaman "punteros inteligentes".

El caso de 'shared_ptr' sirve para compartir una misma dirección de memoria
dinámicamente adjudicada entre varios objetos. Eso sí, la liberación de tal
memoria recae en cada objeto; típicamente, el destructor por defecto
liberará la memoria, pero sólo cuando sea el último y no comparta con otros
objetos. Dicho esto, 'shared_ptr' actúa más como el patrón de la fábrica,
compartiendo la misma dirección de memoria, pero tomando nota de la cuenta
de objetos que la comparten. Cuando la cuenta sea 0 (cero), y por tanto no
haya dueños de tal dirección de memoria, entonces se libera tal memoria.

Por ejemplo, si tengo un miembro de tipo "string", ¿ debería  de usar
> "Shared_Ptr<string>" ???
>
>
No, porque el miembro es un objeto de la clase 'string', no un puntero a
'string'. Si tuvieras esto:

class Nombre
{
private:
  string *pNombre;

  Nombre( const string &nom )
  {
    pNombre = new string( nom );
  }
  ~Nombre()  { delete pNombre; }
};

Entonces sí podrías usar 'shared_ptr<string>'; esto es,

class Nombre
{
private:
  shared_ptr<string> spNombre;

  Nombre( const string &nom )
  {
    spNombre = make_shared<string>( new string( nom ) );
  }
};

Siempre nos interesa construir el puntero compartido; así que haz esto:

shared_ptr<int> sp = new int;

Si no, entonces usa 'make_shared' para seguir esta práctica.

Y en el caso de un vector de objetos de tipo "X", o de un vector " que
> contiene otro vector de objetos de tipo "X" ?
>
>
Es el mismo ejemplo anterior, si no escribes objetos dinámicamente
adjudicados, entonces no necesitas punteros y por tanto no necesitas
punteros inteligentes. Ciertamente, la clase-plantilla 'vector' sí usa
memoria dinámica, pero eso no es tu problema, sino el de 'vector'. Por
ejemplo,

vector< vector<int> > vvi;

vvi.push_back( vector<int>(100) );  // Crea un vector que reserva 100
enteros y agrégalo a 'vvi'.

Ahora bien, si se trata de un vector de vectores dinámicos, entonces sí
usaríamos un puntero inteligente; por ejemplo,

vector< unique_ptr< vector<int> > > vpvi;


Espero que esto te aclare las dudas.

Steven
------------ próxima parte ------------
Se ha borrado un adjunto en formato HTML...
URL: <http://listas.conclase.net/pipermail/cconclase_listas.conclase.net/attachments/20131229/9b85c64b/attachment.html>


Más información sobre la lista de distribución Cconclase