tag ejemplos c++ runtime-error delete-operator

c++ - ejemplos - Doble libre o corrupción... pero ¿por qué?



meta tags ejemplos (6)

Hablemos de copiar objetos en C ++.

Test t; , llama al constructor predeterminado, que asigna una nueva matriz de enteros. Esto está bien, y su comportamiento esperado.

El problema viene cuando empujas t en tu cola usando q.push(t) . Si está familiarizado con Java, C #, o casi cualquier otro lenguaje orientado a objetos, puede esperar que el objeto que creó Earler se agregue a la cola, pero C ++ no funciona de esa manera.

Cuando echamos un vistazo al método std::queue::push , vemos que el elemento que se agrega a la cola se "inicializa a una copia de x". En realidad, es un objeto completamente nuevo que utiliza el constructor de copia para duplicar cada miembro del objeto de Test original para realizar una nueva Test .

¡Tu compilador de C ++ genera un constructor de copia para ti por defecto! Eso es bastante útil, pero causa problemas con los miembros punteros. En su ejemplo, recuerde que int *myArray es solo una dirección de memoria; cuando el valor de myArray se copia del objeto anterior al nuevo, ahora tendrá dos objetos que apuntan a la misma matriz en la memoria. Esto no es intrínsecamente malo, pero el destructor intentará eliminar la misma matriz dos veces, de ahí el error de tiempo de ejecución "doble libre o dañado".

¿Cómo lo arreglo?

El primer paso es implementar un constructor de copia , que puede copiar de forma segura los datos de un objeto a otro. Por simplicidad, podría verse algo como esto:

Test(const Test& other){ myArray = new int[10]; memcpy( myArray, other.myArray, 10 ); }

Ahora, cuando esté copiando objetos de prueba, se asignará una nueva matriz para el nuevo objeto, y también se copiarán los valores de la matriz.

Sin embargo, todavía no estamos completamente fuera de problemas. Hay otro método que el compilador genera para usted que podría llevar a problemas similares: asignación. La diferencia es que con la asignación, ya tenemos un objeto existente cuya memoria debe administrarse de manera adecuada. Aquí hay una implementación de operador de asignación básica:

Test& operator= (const Test& other){ if (this != &other) { memcpy( myArray, other.myArray, 10 ); } return *this; }

La parte importante aquí es que estamos copiando los datos de la otra matriz en la matriz de este objeto, manteniendo separada la memoria de cada objeto. También tenemos un cheque para auto-asignación; de lo contrario, estaríamos copiando de nosotros mismos a nosotros mismos, lo que puede generar un error (no estamos seguros de lo que se supone que debe hacer). Si estuviéramos eliminando y asignando más memoria, la comprobación de autoasignación nos impide eliminar la memoria de la que necesitamos copiar.

#include <queue> using namespace std; class Test{ int *myArray; public: Test(){ myArray = new int[10]; } ~Test(){ delete[] myArray; } }; int main(){ queue<Test> q Test t; q.push(t); }

Después de ejecutar esto, recibo un error de tiempo de ejecución "doble libre o corrupción". Si me deshago del contenido del destructor (la delete ) funciona bien. Que pasa


El problema es que su clase contiene un puntero RAW administrado pero no implementa la regla de tres (cinco en C ++ 11). Como resultado, está obteniendo (se espera) una eliminación doble debido a la copia.

Si está aprendiendo, debe aprender cómo implementar la regla de tres (cinco) . Pero esa no es la solución correcta a este problema. Debe utilizar objetos de contenedor estándar en lugar de tratar de administrar su propio contenedor interno. El contenedor exacto dependerá de lo que esté intentando hacer, pero std :: vector es un buen valor predeterminado (y puede cambiar las contraseñas si no es opimal).

#include <queue> #include <vector> class Test{ std::vector<int> myArray; public: Test(): myArray(10){ } }; int main(){ queue<Test> q Test t; q.push(t); }

La razón por la que debe usar un contenedor estándar es la separation of concerns . Su clase debe preocuparse por la lógica empresarial o la administración de recursos (no ambas). Suponiendo que Test es una clase que está utilizando para mantener el estado de su programa, entonces es lógica de negocios y no debería estar haciendo administración de recursos. Si, por otro lado Test se supone que la Test administre una matriz, entonces probablemente deba aprender más sobre lo que está disponible dentro de la biblioteca estándar.


Es necesario definir un constructor de copia, asignación, operador.

class Test { Test(const Test &that); //Copy constructor Test& operator= (const Test &rhs); //assignment operator }

Su copia que se coloca en la cola apunta a la misma memoria que su original. Cuando se destruye el primero, se borra la memoria. El segundo destruye e intenta borrar la misma memoria.


Obtendrá doble libertad o corrupción porque el primer destructor es para el objeto q en este caso, la memoria asignada por el nuevo será libre. La próxima vez que se llame al detructor para el objeto t en ese momento, la memoria ya estará libre (hecha para q) cuando en destructor eliminar [] myArray; Lo ejecutará se lanzará doble libre o corrupción . La razón es que ambos objetos comparten la misma memoria, por lo que definen / copia, asignación y operador igual a como se mencionó en la respuesta anterior.


También puede intentar marcar nulo antes de eliminar de tal manera que

if(myArray) { delete[] myArray; myArray = NULL; }

o puede definir todas las operaciones de eliminación de una manera segura como esta:

#ifndef SAFE_DELETE #define SAFE_DELETE(p) { if(p) { delete (p); (p) = NULL; } } #endif #ifndef SAFE_DELETE_ARRAY #define SAFE_DELETE_ARRAY(p) { if(p) { delete[] (p); (p) = NULL; } } #endif

y luego usar

SAFE_DELETE_ARRAY(myArray);


Um, ¿no debería el destructor llamar a eliminar, en lugar de eliminar []?