Archivo por meses: agosto 2007

Punteros inteligentes en C++ (smart pointers) 2

En el artículo anterior veíamos como crear una clase simple de puntero inteligente, pero no se mostró ningún ejemplo de su uso. Por este motivo, a continuación pasamos a poner un ejemplo y a ver las deficiencias de dicha clase:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
#include <iostream>
using std::cout;
using std::endl;
 
// Aqui iría nuestra clase de puntero simple
// template...
 
// Clase de ejemplo para mostrar el uso de los punteros inteligentes
class UnaClase {
   UnaClase() {
      cout < < "Soy UnaClase y me estan creando" << endl;
   }
   ~UnaClase() {
      cout << "Soy UnaClase y me están destruyendo" << endl;
   }
   void hacerAlgo() {
      cout << "Soy UnaClase y estoy haciendo algo útil" << endl;
   }
};
 
int main( void ) {
   PtrSimple<UnaClase> ptrUnaClase = new UnaClase();
 
   // Llamamos al método hacerAlgo de UnaClase a través del puntero inteligente.
   ptrUnaClase->hacerAlgo();
 
   // En este punto, C++ destruye la instancia a PtrSimple, eliminando automáticamente
   // la memoria ocupada por este.
}
 
// Para evitar fallos en wordpress:
// 
// </iostream>

Si hacemos la prueba, veremos como el destructor de UnaClase se llama automáticamente al término del programa. Con esto hemos conseguido nuestro primer objetivo, no tener que liberar nosotros mismos la memoria, pero ahora surgen otros problemas: ¿cómo utilizar el puntero en más partes de la aplicación que no sean el propio método?

Antes de continuar vamos a ver una cosita de C++ que nos ayudará mucho: el paso de parametros por referencia. Esto nos permite pasar a un método una referencia a un objeto y si lo modificamos estaremos modificando el objeto original. ¿Cómo se hace esto? pues muy fácil:

1
2
3
4
// Utilizamos nuestra clase UnaClase
void metodo( UnaClase& unaClase ) {
   unaClase.hacerAlgo();
}

Como vemos, al poner los argumentos del método, justo después del tipo se ha añadido un símbolo &. Éste es el que indica que es por referencia. Si no se pone nada sería por valor y si se pusiera un * sería una dirección. De este modo, ya podríamos pasar sin peligro a un método un puntero inteligente:

1
2
3
4
5
6
// Utilizamos nuestra clase UnaClase
void metodo( PtrSimple<unaclase>& unaClase ) {
   unaClase->hacerAlgo();
}
// Para evitar problemas
// </unaclase>

Ahora imaginemos el siguiente caso:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#include <vector>
using std::vector;
// Nuestras definiciones
 
// El codigo
typedef PtrSimple<unaclase> PtrUnaClase;
// Ojo que el worpress cambia algunas cosas (como mayúsculas a minúsculas :@)
vector<ptrunaclase> vectorPunteros;
 
// Utilizamos nuestra clase UnaClase
void metodo( PtrUnaClase& unaClase ) {
   vectorPunteros.add(unaClase);
}
 
// Para evitar problemas
// </ptrunaclase></unaclase></vector>

Estamos haciendo uso de STL para el vector. Este código no funcionará, ya que la clase vector hace uso del constructor de copia de PtrSimple, que no lo hemos definido, por lo que utilizaría el contructor de copia por defecto. Esto derivaría en que en un momento dado el puntero podría liberarse mientras en el vector seguiría existiendo un puntero al mismo, lo que supondrá un fallo de segmentación. Para solventar esto, hay que hacer uso de contadores de uso de punteros, que nos permitirán saber cuantas copias hay del mismo para liberarlo en caso necesario:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
template<typename _t> class PtrSimple2 {
private:
   _t *_ptr;
   int *_count; // Copias del puntero
 
public:
   // Creamos el puntero a partir de una zona de memoria ya existente
   PtrSimple2( _t* ptr = NULL ) {
      _ptr = ptr;
      if( _ptr == NULL )
         _count = NULL;
      else {
         _count = new int;
         *_count = 0;
      }
   }
 
   // Constructor de copia: tomamos un puntero ya existente e incrementamos
   // el contador de usos.
   PtrSimple2( const PtrSimple2<_t>& ptr ) {
      _ptr = ptr._ptr;
      _count = ptr._count;
      if( _count != NULL )
         (*count) = (*count) + 1;
   }
 
   ~PtrSimple2() {
      if( _count != NULL ) {
         (*count) = (*count) - 1;
         // Si nadie apunta a esta zona de memoria,
         // se libera la memoria
         if( (*count) == 0 )
            delete _ptr;
      }
   }
 
   _t* operator->() const {
      return _ptr;
   }
 
   _t& operator*() const {
      return *_ptr;
   }
};
 
// Para evitar fallos en wordpress
// </_t></typename>

Con la clase anterior, ya no debería haber problemas con el tema de STL ni de que hayan copias por ahí… ¿o no? Siguen existiendo casos conflictivos, por ejemplo, tener que pasar sólo la dirección (no el puntero) y luego desde otra parte del código tomar dicho puntero a una clase. Con este caso, perderiamos los contadores de uso que teníamos, por lo que perdemos el control.

A parte de lo anterior, también es constatable que nos faltan funcionalidades: y si quiero comparar dos puntero, y si quiero asignarle una dirección nueva al puntero cuando este ya tenía otra, etc. Esta y otras dudas, para el siguiente artículo.

Punteros inteligentes en C++ (smart pointers)

Para mucha gente, la lacra de programar en C/C++ frente a otros lenguajes es el tener que liberar memoria y controlar donde se libera ésta. Es cierto que el control que dan estos lenguajes al programador es mucho y permite hacer cosas magnificas, pero hay que reconocerlo: somos vagos. En este sentido, se puede hacer en C++ una cosa denominada punteros inteligentes que pueden liberar memoria cuando esta ya no sea necesaria.

Los punteros inteligentes son un tipo de clase que haciendo uso de plantillas son capaces de comportarse como punteros y, segun su implementación, liberar la memoria cuando haga falta. Por contra, esto supone un decremento del rendimiento de la aplicación que habrá que considerar antes de utilizarlos.

Hay una gran cantidad de implementaciones en internet, pero aquí sólo me voy a centrar en la idea básica de todas ellas. Para empezar podemos considerar tres tipos de políticas de manejo de punteros:

  • Agresivas: que consisten en mantener un contador de copias por cada puntero en el sistema (es el mecanismo más lento).
  • Simple: sólo se considera un puntero, no puede asignarse a otros, por lo que no se necesita ningún tipo de contador (es el mecanismo más rápido).
  • Intermedio: cualquier política de uso que se encuentre entre simple y agresiva (su rendimiento será dependiente del tipo de política).

Como ejemplo de puntero que sigue una política simple podemos considerar el siguiente:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
/**
* PtrSimple permite el manejo de la memoria de forma sencilla dentro de un cuerpo de sentencias.
* Los punteros manejados por el no deben ser liberados por el programador ni deberían utilizarse
* de otro modo que no sea a través de esta clase.
*/
template<typename _T> class PtrSimple {
private:
   _T *_ptr;
 
public:
   PtrSimple( _T* ptr = NULL ) {
      _ptr = ptr;
   }
   ~PtrSimple() {
      if( _ptr != NULL )
         delete _ptr;
   }
 
   T* operator->() {
      return _ptr;
   }
 
   T& operator*() {
      return *_ptr;
   }
};
// Esto es para que no me falle en wordpress :P : </typename>

Sin duda este es el tipo de puntero más sencillo que se puede hacer, ya que sólo controla un único puntero, no pudiendo ser asignado a otra clase, lo que limita mucho sus posibilidades a su uso en un único método. A partir de este se puede empezar a trabajar para añadirle funcionalidades (cosa que dejaré para otra entrada).

De momento lo dejo aquí para no alargar más la cosa. A ver si en un par de días saco otro artículo para seguir con el tema. Ahora a esperar que ferdy me crucifique :P

La banda sonora de mi vida

Actualizado 2007-08-04: Ahora pueden escucharse y/o verse las canciones.

Ya que la Reina del Hielo lo piede, habrá que hacer la lista. La verdad es que no tengo muy claro como hacerla, así que he puesto algunas canciones que me describen (en algún momento) y otras que me gustan. La verdad es que generalmente escucho la música y no la letra y me mueve más la sensación que me produce escuchar algo (su ritmo, los sonidos, etc.) que el propio texto de la letra.

Bueno, ahí va la lista:

1.- The Call Of Cthulhu (Metallica)

2.- Auf Achse (Franz Ferdinand)

3.- Amerika (Rammstein)

4.- Ambiente Cadaver (Narco)

5.- Opium (Moonspell)

6.- So Payaso (Extremoduro)

7.- Sonata nº2 para piano (Fréderic Chopin)

8.- Mad World (Gary Jules)

También podéis ver la versión original de Tears for Fear.
9.- Boys Don't Cry (Urban Castle Magic, no confundir con la canción de The Cure)
La canción no tiene vídeo, pero he encontrado este en el que sale :)

10.- a) noid b) when insects cry (Inborn)
Para esta no he encontrado ni video ni nada en la que se pueda escuchar, pero desde jamendo.com se puede descargar libre de royalties (siempre que no sea para uso comercial y no se hagan derivados de la misma).

Los nominados para continuar la lista son: la mujer imaginaria, fluff, sushu y todo aquel que se anime.

Todos en Ciudad Real

En Ciudad Real

Esta semana hemos estado riva, mori, jacobo y yo en Ciudad Real para la fiesta de la Pandorga. La verdad es que con algo de sangre en el alcohol, fuimos capaces de sobrevivir. Eso sí, es el infierno. Por la noche, a eso de la 1:00 la temperatura rondaba los 30ºC, lo que, la verdad, no hacía mucha gracia.

Como yo era el único que llevaba cámara, y no soy muy dado a hacer fotos, he colgado las pocas foto que hice para disfrute de todos. ¿Mori, cómo se puede dormir en esa postura?

Como ya comenta riva, hemos realizado muchas actividades: beber suculentas limonadas, ir al cine, ducharnos (verdad, riva? ;-) ) que no se puede ir como un cerdo por la vida, turismo nocturno, ir al probablemente único garito de heavy que hay en Ciudad Real (el Purgatorio); por desgracia el Poney pisador estaba cerrado :-(, hubiese estado divertido entrar a ver qué tal la ambientación de este :-)

Creo que no podemos quejarnos de casi nada (sólo del calor) y que lo pasamos muy bien (al menos yo, aunque fuese a todas partes con cara de sueño :-P).