Punteros en estructuras

Iniciado por Desiresportal, 31 Octubre 2019, 18:27 PM

0 Miembros y 1 Visitante están viendo este tema.

Desiresportal

Estoy desarrollando alguna cosilla y utilizo una estructura similar a la siguiente para almacenar y manejar datos en binario.

Código (cpp) [Seleccionar]

struct datos {
void *data;
unsigned int length;

datos();
void operator=(datos rDatos);
~datos();
}

datos::datos() {
data = NULL;
length = 0;
}

void datos::operator=(datos rDatos) {
if (rDatos.length > 0 && rDatos.data != NULL) {
length = rDatos.length;
data = new unsigned char[length];
if (data != NULL) {
for (unsigned int a=0; a<length; a++) {
((unsigned char *)data)[a] = ((unsigned char *)rDatos.data)[a];
}
}
}
}

datos::~datos() {
if (data != NULL) {
delete[] (unsigned char *)data;
}
}


Si hago algo como lo siguiente, se me cierra el programa con un error.

Código (cpp) [Seleccionar]

datos miVariable;

// Despues de ponerle datos a la variable....

miFuncion(miVariable);


El caso es que he visto que el problema desaparece cuando el destructor de la estructura no elimina el buffer de datos. Y eso me va a llevar inevitablemente a llenar la memoria. O sino, me lleva a estar pendiente de liberar la memoria en cada programa que haga uso de mi pequeña libreria.

¿Porque ocurre esto? ¿Como puedo hacer correctamente el operador de copia y el destructor de la estructura para manejarlo como una variable mas? (O al menos como un string, por ejemplo)

Muchas gracias por adelantado.

ThunderCls

#1
Si no me equivoco el error que obtienes es al hacer un double "delete" sobre la misma memoria dinamica. Primero tu sobrecarga del operador de asignacion no deberia trabajar con copias de los objectos sino con referencias:

Código (cpp) [Seleccionar]
void datos::operator=(datos rDatos){}

deberia ser

Código (cpp) [Seleccionar]
void datos::operator=(const datos &rDatos){}

Tambien te aconsejaria que para poder usar asignaciones en cadena modifiques tu funcion a

Código (cpp) [Seleccionar]
datos &datos::operator=(const datos &rDatos)
{
   // ...
   return *this;
}


Luego, mi teoria de tu error es que al enviar tu estructura como parametro a la funcion lo estas haciendo por valor (por defecto), lo que crea una copia de los valores de dicha estructura en memoria al pasar a dicha funcion, cuando el tiempo de vida del parametro termina al salir de la funcion, se invoca a tu destructor sobre el mismo puntero de tu estructura original (ambos punteros apuntan a la misma memoria), luego al terminar tu funcion principal y concluir el ambito de tu variable de estructura original, se volvera a destruir el objeto llamando a tu destructor el cual intentara realizar otro "delete" sobre la misma posicion de memoria ya liberada antes, provocando una excepcion.

Posibles soluciones
Solucion 1: Intenta pasar tu estructura a la funcion por referencia y cuenta que tal:

Código (cpp) [Seleccionar]
miFuncion(miVariable);

void miFuncion(const datos &miVariable)
{
   datos miOtraVariable;
   
   miOtraVariable = miVariable;
}


Solucion 2: Create un constructor de copia para tu estructura

Código (cpp) [Seleccionar]
datos::datos(const datos &copy)
{
   // tu copia aqui
}

Saludos
-[ "...I can only show you the door. You're the one that has to walk through it." – Morpheus (The Matrix) ]-
http://reversec0de.wordpress.com
https://github.com/ThunderCls/

Loretz

Cita de: Desiresportal en 31 Octubre 2019, 18:27 PM
Estoy desarrollando alguna cosilla y utilizo una estructura similar a la siguiente para almacenar y manejar datos en binario.

Código (cpp) [Seleccionar]

struct datos {
void *data;
unsigned int length;

datos();
void operator=(datos rDatos);
~datos();
}


Si tu struct datos es responsable del manejo de un recurso de tipo void*, probablemente necesites usar templates en su lugar, o tener muy en claro el significado y funcionamiento de las funciones especiales de una clase. Y me da la impresión de que no es el caso.

En el destructor pones
delete[] (unsigned char *)data;
de modo que estás forzando a que data sea de tipo unsigned char*, ya no void*, lo mismo cada vez que necesitas hacer ese cast.

Tu problema no está (solamente) en el operador de asignación, sino en la idea misma de manejar un recurso con una clase C++ y, si no te molesta un consejo de tipo general, necesitas un buen libro y estudiar seriamente, no es preguntando sobre errores al azar que se aprende un lenguaje de programación complejo; realmente, con este sistema te tomará años convertirte en un programador mediocre.

Desiresportal

Ya esta solucionado. Muchisimas gracias.

Resulta que al pasar la estructura a la funcion, independientemente de si lo hago por referencia o por copia, la funcion hacia uso del constructor de copia por defecto. Yo creia (y mal creido porque nunca hice pruebas ni encontré documentacion para entenderlo) que usaba el "=".

CitarEn el destructor pones
Código (cpp) [Seleccionar]
delete[] (unsigned char *)data;
de modo que estás forzando a que data sea de tipo unsigned char*, ya no void*, lo mismo cada vez que necesitas hacer ese cast.

Utilizo el tipo "void*" porque en esta estructura almaceno textos, enteros, decimales y puede que termine almacenando incluso estructuras. Es el tipo que veo cuando utilizo "fwrite()" y creía que era el apropiado para ir haciendo cast.

Ademas, tengo entendido que los cast son cosa del compilador y que en el programa resultante no existe carga extra por usarlo. ¿Me equivoco?

En cuanto a un libro, claro que me lo estudiaría de cabo a rabo. Aunque tienen varias pegas:
- Cuestan una pasta que no tengo (y en las bibliotecas publicas no hay o son de los que no profundizan nada)
- Lo explican todo de una forma muy dificil de digerir (incluso investigando un poco para arreglar este problema que ya he solucionado, no he encontrado nada que fuese facil de entender)
- Los mejores están en inglés y cualquier cosa que no sea codigo (a veces incluso el codigo) no la entiendo

En resumen:
Tema resuelto. Muchas gracias por la ayuda.

ThunderCls

Cita de: Desiresportal en  1 Noviembre 2019, 13:15 PM
Resulta que al pasar la estructura a la funcion, independientemente de si lo hago por referencia o por copia, la funcion hacia uso del constructor de copia por defecto. Yo creia (y mal creido porque nunca hice pruebas ni encontré documentacion para entenderlo) que usaba el "=".

En C++ siempre al pasar un objeto como parametro por valor a una funcion se invoca al constructor de copia de dicho objeto. Si el programador no tiene uno definido, el compilador genera un constructor de copia por defecto, el tema esta en que dicho constructor hace "shallow copy" y en este caso no es lo ideal por lo que ya explique anteriormente. Como no sabia lo que estabas intentando hacer ni las restricciones con las que estabas escribiendo tu codigo te di dos posibles soluciones, una de ellas era que te crearas tu propio constructor de copia usando "deep copy".

Cita de: Desiresportal en  1 Noviembre 2019, 13:15 PM
En cuanto a un libro, claro que me lo estudiaría de cabo a rabo. Aunque tienen varias pegas:
- Cuestan una pasta que no tengo (y en las bibliotecas publicas no hay o son de los que no profundizan nada)
- Lo explican todo de una forma muy dificil de digerir (incluso investigando un poco para arreglar este problema que ya he solucionado, no he encontrado nada que fuese facil de entender)
- Los mejores están en inglés y cualquier cosa que no sea codigo (a veces incluso el codigo) no la entiendo

- Si sabes usar un buscador en internet no necesitas pagar una fortuna por ningun libro
- Estos temas no son temas basicos o de principiantes, se supone que domines el lenguaje y varios conceptos llegado a este punto
- Te aconsejaria empezar a estudiar ingles si quieres dedicarte profesionalmente a esto o si deseas llegar a algun lado con la programacion y las tecnologias en general. Para todo lo demas esta Google Translator

Cita de: Desiresportal en  1 Noviembre 2019, 13:15 PM
En resumen:
Tema resuelto. Muchas gracias por la ayuda.

Me alegro. Suerte
-[ "...I can only show you the door. You're the one that has to walk through it." – Morpheus (The Matrix) ]-
http://reversec0de.wordpress.com
https://github.com/ThunderCls/