#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 esta, me sale un error en tiempo de ejecución «doble libre o la corrupción». Si me deshago de el destructor de contenido (el delete) funciona bien. Qué tiene de malo?

  • Leer esto y leer esto.
  • Test t(); declara una función, el código no compilará. usted necesita para enviar el código real.
  • Mientras el pase de diapositivas en el segundo enlace tiene algunos puntos buenos, no estoy de acuerdo con todos ellos, en particular el ejemplo en la página 9 mostrando un local enorme objeto que se va a devolver un valor. Que invoca copia de la construcción que puede ser costoso, a menos que usted está utilizando C++11 de mover la semántica.
  • La presentación habla sobre punteros inteligentes como std::unique_ptr, que son parte del estándar C++11. Creo que es seguro asumir que es lo que implica mover la semántica, al menos, si no RVO.
  • Leer Regla de Tres
  • std::unique_ptr es nuevo en C++11, pero std::shared_ptr no lo es. También, la presentación de diapositivas menciona std::array, que no es nuevo en C++11, tampoco. Así que no creo que se mueva la semántica puede ser asumida. Incluso con RVO usar, asignación podría ser invocada sin C++11 de mover la asignación.
  • Ambos std::shared_ptr y std::array son sólo de C++11. Puede haber cosas similares en impulso, pero estas no tienen un std:: prefijo.
  • en realidad, se introdujeron en TR1 y actualizado en C++11 para que coincida aumentar la funcionalidad.
  • Justo lo suficiente. Yo nunca hizo uso de TR1, pero entiendo tu punto de vista. Incluso entonces, sin embargo, sería en el tr1 espacio de nombres, no std 😉
  • bien jugado 😛
  • Hace esto, incluso compilar? No Test t(); ser Test t?
  • Fijo. Se compila y se tiene el mismo problema ahora.
  • Por desgracia, las respuestas que aparecen a continuación son el camino equivocado para solucionar este problema. Usted debe hacer myArray un std::vector<int> en lugar de un int*, a continuación, todos los problemas son resueltos (incluida la correcta C++ mover los constructores están incluidos en std::vector).

InformationsquelleAutor Mihai Neacsu | 2012-12-28

6 Comentarios

  1. 85

    Vamos a hablar acerca de cómo copiar objetos en C++.

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

    Problema viene cuando usted empuje t en su cola con q.push(t). Si estás familiarizado con Java, C#, o casi cualquier otro lenguaje orientado a objetos, se puede esperar que el objeto creado indicadores para ser añadido a la cola, pero en C++ no funciona de esa manera.

    Cuando echamos un vistazo a std::cola::push método, vemos que el elemento que se agrega a la cola es «inicializa a una copia de x». Es en realidad un nuevo objeto que utiliza el constructor de copia para duplicar cada miembro de su original Test objeto de hacer una nueva Test.

    El compilador de C++ genera un constructor de copia por defecto! Eso es bastante útil, pero a causa de problemas con el puntero miembros. En tu ejemplo, recuerde que int *myArray es una dirección de memoria; cuando el valor de myArray se copia desde el objeto antiguo a uno nuevo, ahora vamos a tener dos objetos que apunta a la misma matriz en la memoria. Esto no es intrínsecamente malo, pero el destructor continuación, intenta eliminar la misma matriz de dos veces, de ahí la «doble libre o la corrupción» error de tiempo de ejecución.

    ¿Cómo puedo solucionarlo?

    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 ser algo como esto:

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

    Ahora cuando se van a copiar objetos de Prueba, una nueva matriz será asignado para el nuevo objeto, y los valores de la matriz se copiarán así.

    No estamos completamente fuera de problemas, pero. Hay otro método que el compilador genera para usted que podría conducir a problemas similares – asignación. La diferencia es que con la asignación, ya tenemos un objeto existente, cuya memoria debe ser manejado adecuadamente. Aquí tenemos un operador de asignación aplicación:

    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 matriz en este objeto de la matriz, manteniendo cada uno de los objetos de la memoria independiente. También tenemos un cheque para la auto-asignación; de lo contrario, estaríamos copia de nosotros mismos para nosotros mismos, lo que puede producir un error (no se sabe de qué se debe hacer). Si fuéramos a eliminar y asignar más memoria, la auto-asignación de verificación nos impide borrar la memoria de la que necesitamos copiar.

    • Me fijo la pregunta original. Así que me fijo en esto como bien Test t(); no es una declaración de variable, pero hacia adelante y a la declaración de una función.
    • Explicación correcta de lo que está mal y cómo arreglarlo (si se va a solucionarlo). Pero es una mala solución para el problema. La solución correcta en cualquier código real es cambiar el tipo de los estados myArray a std::vector<int>. De esta manera usted correctamente obtener más eficiente de mover los componentes (constructor/asignación) que se definen en C++11.
    • Buen punto, pero creo que es más importante para ilustrar el problema real y cómo tradicionalmente solucionarlo. Una vez que se entiende, es obvio por qué el vector es una mejor solución.
    • Diría usted que el compilador hizo una copia superficial frente a una copia en profundidad, que es lo que el código tiene que hacer?
    • Sí, es una copia superficial. La parte importante es cómo el compilador del comportamiento hace que este tipo de error de memoria, porque en el fondo de las copias no son siempre el resultado deseado.
    • De hecho. Yo estaba tratando de pensar en términos de lo que el código quería hacer y lo que el compilador realmente lo hizo. Gracias!
    • Por qué no escribir delete[] myarray; en el operador de asignación sobrecargado? de lo contrario, no hay pérdida de memoria. ¿No es así? Me corrija Si estoy equivocado
    • No hay una pérdida de memoria debido a que el objeto en el lado derecho de la asignación se puede todavía ser utilizado después de esta asignación. Esta aplicación copia el valor de myArray en el nuevo objeto de la matriz, en lugar de transferir el real de la matriz entre los objetos. La matriz será liberado por other‘s destructor. Si desea mover la matriz entre los objetos, entonces es posible que desee ver en el mover semántica: stackoverflow.com/questions/3106110/what-are-move-semantics
    • Este está roto. memcpy copias bytes no enteros. Mejor usar std::copy (igual de eficiente, pero más seguro).

  2. 16

    El problema es que la clase contiene un administrado RAW puntero, pero no aplicar la regla de tres (cinco en C++11). Como resultado, usted está consiguiendo (como era de esperar, un doble eliminar porque han copiado.

    Si usted está aprendiendo que usted debe aprender a aplicar la regla de tres (cinco). Pero esa no es la solución correcta a este problema. Usted debe estar utilizando el estándar de contenedor de objetos en lugar de tratar de manejar sus propios interna del contenedor. La exacta recipiente dependerá de lo que usted está tratando de hacer, pero std::vector es un buen valor por defecto (y puede cambiar epílogos si es que no 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, usted debe utilizar un contenedor estándar es el separation of concerns. La clase debería ser una cuestión de lógica de negocio o la gestión de los recursos (no ambos). Suponiendo Test es la clase que se utiliza para mantener un estado acerca de su programa, a continuación, es la lógica de negocio y que no debería estar haciendo la gestión de los recursos. Por otro lado, si Test se supone gestionar una matriz, entonces probablemente usted necesita para aprender más acerca de lo que está disponible dentro de la biblioteca estándar.

  3. 4

    Usted está recibiendo doble libre o la corrupción porque primero destructor para el objeto q en este caso la memoria asignada por nueva será gratuita.La próxima vez, cuando detructor será llamado por objeto t en ese momento la memoria ya es libre (hecho por q) por lo tanto, cuando en el destructor delete[] myArray; ejecutará lanzará doble libre o la corrupción.
    La razón es que tanto el objeto de compartir la misma memoria para definir \copia, cesión, y el operador igual como se mencionó en la respuesta anterior.

  4. 3

    Necesita definir un constructor de copia, cesión, operador.

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

    Su copia que se inserta en la cola apuntando a la misma memoria que el original. Cuando la primera se destruye, elimina de la memoria. La segunda destructs y trata de eliminar la misma de la memoria.

    • Operador igual?
    • No necesita de la igualdad.
    • El operador igual que Ryan se refiere es likel el operador de asignación. Es necesario definir un constructor predeterminado, un constructor de copia y un copia de asignación operador. Si usted está usando C++11, también se puede definir un mover contructor y mover asignación operador.
    • Lo siento, no la necesita, pero cuando se comparan para la igualdad usted está por lo general después de que el contenido de lo que los punteros punto a, no la ubicación de la memoria para incluir la mayor parte del tiempo el uso de punteros.
  5. 0

    También puede probar para comprobar null antes de eliminar tales que

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

    o puede definir todas las operaciones de eliminación de ina 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, a continuación, utilizar

    SAFE_DELETE_ARRAY(myArray);
    • La comprobación de null es redundante porque al llamar a delete con un puntero nulo no hacer nada. También establecer el puntero a null después de la eliminación es redundante en un destructor porque será invalidado cuando la función termina.
  6. -2

    Um, no el destructor de estar llamando a eliminar, en lugar de eliminar[]?

    • No, fue asignado con new[], ergo se necesita una delete[].
    • Noooooooo! Es justamente llamada delete [] myArray. Ver la nueva, no es new int pero new int [x].
    • parashift.com/c++-faq/delete-array.html por Favor, lea esto si usted no entiende lo que los comentarios están hablando, y por qué usted está consiguiendo votada abajo (por cierto, yo no downvote).

Dejar respuesta

Please enter your comment!
Please enter your name here