Mostrando entradas con la etiqueta shared pointers. Mostrar todas las entradas
Mostrando entradas con la etiqueta shared pointers. Mostrar todas las entradas

sábado, 29 de mayo de 2010

Shared Pointers de C++0x

En C usted puede hacer esto:
#include <stdlib.h>

struct Persona { };

int main()
{
  Persona* a = malloc(sizeof(Persona));
  free(a);
  return 0;
}

Un ejemplo equivalente en C++:
class Persona { };

int main()
{
  Persona* a = new Persona();
  delete a;
  return 0;
}

Por cada malloc existe un free (al menos que use reallocs), y por cada new existe un delete (y por cada new[] un delete[]).

¿Existe una forma por la cual C++ se "entere" que ya no quiero usar un puntero? La respuesta es: No, no existe. C++ no tiene un garbage collector. Pero existen clases que pueden ayudarnos, como el viejo y tan poco querido auto_ptr, o los mejorados unique_ptr, shared_ptr y weak_ptr del nuevo estándar de C++0x (o el TR1).

Un shared pointer es una clase que se encarga de guardar un puntero a un objeto (o tipo de dato), y cuenta la cantidad de referencias que se están haciendo a dicho objeto (es decir, la cantidad de shared pointers que apuntan al mismo objeto). El último shared pointer que se destruya (cuando las referencias llegan a cero), será el encargado de borrar el objeto apuntado (mediante un simple delete).

Un ejemplo:
#include <iostream>
#include <memory>  // Aquí debería estar shared_ptr<> (GCC 4.4)

using namespace std;

class Persona {
  int n;

public:
  Persona(int n) : n(n) {
    cout << "Nace la persona " << n << "\n";
  }

  ~Persona() { 
    cout << "Muere la persona " << n << "\n";
  }

  static shared_ptr<Persona> Crear(int n) { 
    return shared_ptr<Persona>(new Persona(n));
  }
};

int main()
{
  shared_ptr<Persona> a(new Persona(1));
  shared_ptr<Persona> b = Persona::Crear(2);
  shared_ptr<Persona> c;

  cout << "--- Aquí ambas personas existen ---\n";

  c = a; // Aquí c apunta a la persona 1
  b = c; // Ahora b apuntará a la persona 1 (la persona 2 muere porque
         // ya no existen referencias a ella)

  cout << "--- Aquí la persona 2 ya no existe ---\n";
  
  return 0;
}        // Aquí muere la persona 1 (a, b, c apuntaban a ella)
La salida del anterior programa es esta:
Nace la persona 1
Nace la persona 2
--- Aquí ambas personas existen ---
Muere la persona 2
--- Aquí la persona 2 ya no existe ---
Muere la persona 1
Como puede ver, en el anterior programa se llama sólo dos veces a "new Persona" y dos veces al destructor ~Persona. No debemos preocuparnos por usar "delete", el shared_ptr<> hace todo por nosotros.

¿Cómo hago para que la clase shared_ptr funcione en VS2008 SP1 Express? Debe definir _HAS_TR1 antes de incluir el archivo <memory>. Ejemplo:
#ifdef _MSC_VER      // Si estamos usando el compilador de Microsoft
  #define _HAS_TR1 1 // Esto hará que se incluyan las clases del TR1 (std::tr1)
#endif

#include <iostream>
#include <memory>

using namespace std;
#ifdef _MSC_VER
  using namespace std::tr1; // para tener shared_ptr<> disponible
#endif

// Resto del ejemplo...