PDF de programación - lkptr.h: Programación por Referencia Java para C++

Imágen de pdf lkptr.h: Programación por Referencia Java para C++

lkptr.h: Programación por Referencia Java para C++gráfica de visualizaciones

Actualizado el 18 de Mayo del 2018 (Publicado el 3 de Mayo del 2017)
1.010 visualizaciones desde el 3 de Mayo del 2017
83,6 KB
10 paginas
Creado hace 12a (05/11/2011)
lkptr.h: Programación por Referencia Java para C++

Universidad de Costa Rica, Escuela de Ciencias de la Computación e Informática

Adolfo Di Mare

San José, Costa Rica

[email protected]

Resumen. La recolección automática de memoria dinámica hace
la
programación Java más fácil que la programación C++. Al usar la biblioteca
"lkptr<X>" aquí descrita se pueden obtener muchas de las ventajas de Java
sin necesidad de incorporar un recolector de basura para C++.

Palabras clave: Cuenta de referencia, memoria dinámica, ingeniería de
sistemas, implementación.

1 Programación con punteros inteligentes

La biblioteca STL de C++ incluye la clase "auto_ptr<X>" en el archivo de
encabezado <memory>. La clase "auto_ptr<X>" le permite al programador cliente usar
punteros inteligentes que destruyen el objeto que referencian cuando su ámbito de
existencia termina; estos punteros son "dueños" del objeto que referencian. Por
ejemplo, en el siguiente bloque de código el programador no tiene que preocuparse
por retornar la memoria dinámica de la que el puntero "p" es dueña:
{ auto_ptr<BigMatrix> p , q( new BigMatrix( 50*1000 , 100*1000 ) ) ;
// ...
q->foo( 15*1000 ); // "q" funciona como un puntero
p = q; // Ahora "q" es NULL y "p" es el nuevo dueño
// ...
} // Aquí el destructor de "p" devuelve la memoria

punteros fuera similar a la siguiente [6]:

La clase "auto_ptr<X>" funciona como si la implementación de la asignación de
template <class T> // OJO: "q" no es un argumento "const"
auto_ptr<T>& auto_ptr<T>::operator= ( auto_ptr<T>& q ) {
if ( this != &q ) {
delete m_ptr; // "m_ptr" es el campo que referencia el objeto
m_ptr = q.m_ptr; // nuevo dueño
q.m_ptr = NULL; // deja a "q" en NULL
}
return *this;
}
La clase "auto_ptr<X>" tiene algunas desventajas bien conocidas entre la que
destaca el que estos punteros inteligentes no se deben usar para almacenar valores en
los contenedores de la biblioteca STL porque cada puntero "auto_ptr<X>" siempre es
el único dueño del objeto que referencia. La operación de inserción en cualquier
contenedor STL siempre almacena una copia profunda del objeto pues se supone que
la copia almacenada puede ser modificada sin cambiar el valor original [7]. En el

ejemplo de arriba, la asignación "p = q;" deja al puntero "q" con un valor nulo
("NULL") de manera que el objeto referenciado no sea destruido 2 veces (la primera
una vez por el destructor de "p" y la segunda por el de "q").

La forma de remediar las carencias de la clase "auto_ptr<X>" es usar cuentas de
referencia de manera que varios punteros inteligentes puedan compartir la misma
instancia. Existen varias implementaciones de las cuentas de referencia, pero la
implementación de la clase "lkptr<X>" escogida aquí es la llamada "enlaces de
referencia" (reference linking), que tiene las siguientes ventajas:

 No usa memoria dinámica adicional.
 No es "intrusiva" porque no requiere de un contador almacenado en el objeto

 Es viable usar contenedores STL para almacenar referencias inteligentes

referenciado.

"lkptr<X>".

encabezado "lkptr.h".

 Es muy simple de usar pues la implementación completa está en el archivo de

 Es adecuada para aplicaciones en que se usa programación concurrente

mediante múltiples hilos o procesos.

2 Implementación de la clase "lkptr<X>"

Los punteros inteligentes "lkptr<X>" tienen la cualidad de que comparten un mismo
objeto. Una forma de lograr que varios punteros queden marcados porque referencian
el mismo objeto es incluir en el objeto referenciado un contador que indica cuántos
punteros le referencian. La cuenta se incrementa si a un puntero inteligente se le
asigna el valor de otro y el destructor del puntero inteligente es el responsable de
decrementar la cuenta; en este caso, si la cuenta llegara a ser cero, también el
destructor del puntero debe destruir el objeto referenciado porque ya ningún otro
puntero inteligente la está referenciando.



+---+
p1 | *-|----->+
+---+ |
| RefCnt
+---+ +---->+---+----------------------+
p2 | *-|----------->| 3 | Costa Rica Pura Vida |
+---+ +---->+---+----------------------+
|
+---+ |
p3 | *-|----->+
+---+
RefCnt
+---+ +---+----------------------+
p4 | *-|----------->| 1 | Viva Italia |
+---+ +---+----------------------+

Lkptr<Chunche> foo() {
lkptr<Chunche> p1, p2, p3;
p3.reset( new Chunche( "Costa Rica Pura Vida" ) );
p1 = p2 = p3; // los 3 comparte la instancia
{ lkptr<Chunche> p4 (new Chunche( "Viva Italia" ) );
// ...
} // aquí "p4" es destruído

return p2; // p1, p2, p3 son destruidos
} // ... el objeto referenciado persiste

Figura 1

En la Figura 1 los punteros "p1", "p2" y "p3" comparten el mismo objeto "Costa
Rica Pura Vida" mientras que el único dueño de "Viva Italia" es "p4". Si el
puntero "p4" dejara de existir, lo que en este caso ocurre cuando el control del
programa deja el bloque en que está definido "p4", su valor referenciado también es
destruido porque su cuenta de referencia llega a cero. Esto no es lo que ocurre cuando
los otros 3 punteros son destruidos porque para retornar el valor de "p2" el compilador
debe invocar al constructor de copia de la clase "lkptr<X>" para retornar una copia
de "p2", y eso aumenta la cuenta de referencia a 4. A pesar de que el destructor de los
3 punteros decrementa el contador de referencias 3 veces, a fin de cuentas tiene valor
1 == 4-3 y, en consecuencia, el valor compartido no es destruido y sí es
correctamente retornado al programa invocador.



+---+
p1 | *-|-->+
+---+ |
| RefCnt
+---+ +-->+---+---+ +----------------------+
p2 | *-|------>| 3 | *-|-->| Costa Rica Pura Vida |
+---+ +-->+---+---+ +----------------------+
|
+---+ |
p3 | *-|-->+
+---+ RefCnt
+---+ +---+---+ +----------------------+
p4 | *-|------>| 1 | *-|-->| Viva Italia |
+---+ +---+---+ +----------------------+

Figura 2

El problema que tiene la implementación de punteros con cuentas de referencia es
que hay que incluir en el objeto referenciado un campo para almacenar ahí la cantidad
de punteros que referencian el objeto. Otra forma de lograr lo mismo es poner esa
cuenta en otro objeto externo como se muestra en la Figura 2. Este esquema no
siempre es deseable porque requiere crear un objeto intermedio en memoria dinámica
lo que en algunas aplicaciones puede ser inconveniente, como en aplicaciones de
sistemas de tiempo real en donde crear objetos en memoria dinámica puede ser muy
oneroso.

La otra manera de realizar la implementación es poner en una lista doblemente
enlazada a todos los punteros que comparten un valor; se sabe que un puntero es la
última referencia si está solo en la lista, como ocurre con "p4" en la Figura 3
(siguiente página). La desventaja de esta solución es que requiere tres veces más
almacenamiento por puntero, pues además de la referencia al valor compartido es
necesario incluir los 2 punteros necesarios para mantener la lista doblemente
enlazada. Sin embargo, en la mayor parte de las aplicaciones este incremento en la
cantidad de memoria es poco relevante porque serán relativamente pocos los punteros

que existan simultáneamente en un programa. La implementación "lkptr<X>" que se
discute en este trabajo usa la lista enlazada en lugar de las cuentas de referencia
porque es la que tiene mayores ventajas y menos inconvenientes. No importa cuál es
el primero o el último de la lista, pues lo que importa es que la asignación de un
puntero a otro resulta también en el enlace en la lista de referencias.

<=================================> <=============>
| p1 p2 p3 | | p4 |
| +-+-+ +-+-+ +-+-+ | | +-+-+ |
<===>|*|*|<===>|*|*|<===>|*|*|<===> <===>|*|*|<===>
+-+-+ +-+-+ +-+-+ +-+-+
| * | | * | | * | | * |
+-|-+ +-|-+ +-|-+ +-|-+
| | | |
\|/ \|/ \|/ \|/
+----------------------+ +----------------------+
| Costa Rica Pura Vida | | Viva Italia |
+----------------------+ +----------------------+

Figura 3



3 Construcción de una clase con punteros inteligentes

Una de las desventajas que tiene C++ cuando se le compara con Java es que es
relativamente más caro retornar objetos grandes en C++. Por ejemplo, si se usa la
clase "Matrix" descrita en [3], la forma natural de implementar los operadores
aritméticos es la siguiente:

/// Calcula y retorna \c (A * B).
Matrix operator* ( const Matrix& A, const Matrix& B ) {
Matrix Resultado...
// ... algoritmo de multiplicación de matrices
return Resultado;
}
Como la variable "Resultado" es una variable local, el compilador genera código
para copiar con el constructor de copia esa variable local al área de retorno en la
rutina invocadora, lo que en este caso implica hacer la copia de un objeto bastante
grande, pues la cantidad de memoria que usa una matriz es proporcional a la
multiplicación de sus dimensiones. Lo natural sería crear la matriz en memoria
dinámic
  • Links de descarga
http://lwp-l.com/pdf3293

Comentarios de: lkptr.h: Programación por Referencia Java para C++ (0)


No hay comentarios
 

Comentar...

Nombre
Correo (no se visualiza en la web)
Valoración
Comentarios...
CerrarCerrar
CerrarCerrar
Cerrar

Tienes que ser un usuario registrado para poder insertar imágenes, archivos y/o videos.

Puedes registrarte o validarte desde aquí.

Codigo
Negrita
Subrayado
Tachado
Cursiva
Insertar enlace
Imagen externa
Emoticon
Tabular
Centrar
Titulo
Linea
Disminuir
Aumentar
Vista preliminar
sonreir
dientes
lengua
guiño
enfadado
confundido
llorar
avergonzado
sorprendido
triste
sol
estrella
jarra
camara
taza de cafe
email
beso
bombilla
amor
mal
bien
Es necesario revisar y aceptar las políticas de privacidad