[C con Clase] Serializar variables

Steven Davidson srd4121 en njit.edu
Sab Mar 13 18:39:52 CET 2010


Hola Ferrán,

Ferran Ferri wrote:
> Hola Steven,
> muchas grecias por tu respuesta. Mi forma de hacerlo cuando fallaba
> era asi:
> 
> void toArray(unsigned char* data)
> {
> 	int a = 3;
> 	long b = 444;
> 	int *pA = &a;
> 	long *pB = &b;
> 
> 	cout << "Valor de *pA " <<*pA <<endl;
> 	memcpy(data,(unsigned char*)pA,sizeof(int));
> 	data+=sizeof(int);
> 	cout << "Valor de *pB " <<*pB <<endl;
> 	memcpy(data,(unsigned char*)pB,sizeof(long));
> 	data+=sizeof(long);
> }
> Y fallaba por el incremento de data (lo hacia mal aunque no se por
> que. Asi se corregia:
> 

No. No es cierto. He probado tu función y funciona (valga la 
redundancia) perfectamente.

> void toArray(unsigned char* data)
> {
> 	int a = 3;
> 	long b = 444;
> 	int *pA = &a;
> 	long *pB = &b;
> 
> 	cout << "Valor de *pA " <<*pA <<endl;
> 	memcpy(data,(unsigned char*)pA,sizeof(int));
> 	cout << "Valor de *pB " <<*pB <<endl;
> 	memcpy(data + sizeof(int),(unsigned char*)pB,sizeof(long));
> 	data+=sizeof(long);
> }
> 

Esta versión es prácticamente la misma que la primera. Eso sí, la última 
asignación no es necesaria:
datos += sizeof(long);

ya que hemos terminado.

> Tu solucion, sin embargo, me parece mucho mas elegante. Pero me
> genera dudas. Que diferencia hay entre reinterpret cast y static cast
> ademas de que static cast fuerza el que se haga en tiempo de

El operador 'reinterpet_cast' sirve para dejar la secuencia de bits 
intacta. Esto significa que el tipo de dato de tal secuencia de bits 
varía. El operador 'static_cast' puede modificar los bits yendo de un 
tipo a otro.

> compilacion? Y la segunda duda es que cuando se acumulan variables no
> puedo ir sumando cada vez para ponerme en la posicion del array.
> Serviria algo como
> *reinterpret_cast< int * >( data++ ) = a;
> *reinterpret_cast< long * >( data++ ) = b;  // si tuvieramos que meter
> mas variables
> Al haber reinterpretado el puntero, y hacer post incremento,
> avanzariamos un int en la primera sentencia y un long en la segunda,
> no es asi?
> 

No. Nunca se cambia el tipo de la variable sino el tipo del valor. La 
variable 'data' ha sido definida como 'unsigned char *' y nunca se puede 
cambiar. Lo que sí podemos hacer es acceder a su valor y cambiar su 
tipo. En la práctica, el compilador generará una variable temporal con 
el tipo indicado y con el valor original. Por ejemplo,

double b = -1.3;
int a = static_cast< int >( b );

El compilador internamente hará algo así:

double b = -1.3;
int __temp = static_cast< int >( -1.3 );
int a = __temp;

Como puedes ver, 'b' nunca cambiará ni tampoco el valor que contiene. Lo 
que se hace es crear otro valor basándose en el valor de 'b', pero sin 
modificar éste.

Dicho todo esto, la respuesta a tu pregunta es que al incrementar 
'data', este puntero apuntará al siguiente elemento. Como cada elemento 
es 'unsigned char', el incremento será al siguiente 'char' que equivale 
al siguiente byte. Para hacer el incremento a la siguiente dirección de 
memoria posterior a 'int', tendrías que sumar ese tamaño en byte, ya que 
'char' siempre ocupa 1 byte. Esto es,

*reinterpret_cast< int * >( data ) = a;
data += sizeof(int);

*reinterpret_cast< long * >( data ) = b;
data += sizeof(long);

> La tercera es mas offtopic. Si la funcion toArray la tengo en una
> clase (es un metodo), donde deberia hacer la reserva de memoria. El
> borrado es responsabilidad de quien usa la funcion cuando ya no la
> necesita. Pero la creacion podriamos hacerla dentro de la funcion
> (esta seria mi eleccion), puesto que ya sabemos cuanto espacio
> necesitamos, o exigir que se cree fuera, y comprobar que o sea null,
> puesto que tenemos que separar responsabilidades. Que opinais?

No es una buena práctica crear efectos secundarios en una función. Lo 
que propones justamente crea este efecto secundario de adjudicar memoria 
dinámicamente. Aconsejo que el array se cree fuera y así delegamos la 
responsabilidad al invocador de esta función miembro.

Claro está, al usar clases, recomendaría usar un objeto de una clase 
contenedora y si esta clase usa memoria dinámicamente, entonces mejor 
que mejor. Por ejemplo, puedes usar la clase-plantilla 'vector'. Por 
otro lado, posiblemente esto no sea lo que quieres, ya que 'toArray()' 
no representaría un array fundamental, sino un objeto de la clase 
'vector<unsigned char>'.


Espero haber aclarado las dudas.

Steven





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