std::list<Node *> lst;
//....
Node * node = /* get from somewhere pointer on my node */;
lst.remove(node);

Hace std::list::quitar llamada al método destructor(y la memoria libre) de cada elemento eliminado? Si sí, ¿cómo puedo evitarlo?

  • ¿Por qué te almacenar punteros en el primer lugar?

6 Comentarios

  1. 44

    Sí, la eliminación de un Foo* de un contenedor destruye la Foo*, pero no va a liberar el Foo. La destrucción de un raw puntero es siempre un no-op. No puede ser de otra manera! Permítanme darles algunas razones por qué.

    De clase de almacenamiento

    La eliminación de un puntero sólo tiene sentido si el pointee en realidad fue asignada dinámicamente, pero ¿cómo podría el tiempo de ejecución puede saber si ese es el caso cuando la variable de puntero es destruido? Los punteros pueden apuntar también a la estática y las variables automáticas, y la eliminación de uno de esos rendimientos un comportamiento indefinido.

    {
        Foo x;
        Foo* p = &x;
    
        Foo* q = new Foo;
    
        //Has *q been allocated dynamically?
        //(The answer is YES, but the runtime doesn't know that.)
    
        //Has *p been allocated dynamically?
        //(The answer is NO, but the runtime doesn't know that.)
    }

    Colgando punteros

    No hay manera de averiguar si los pointee ya ha sido lanzado en el pasado. Borrar el mismo puntero dos veces rendimientos un comportamiento indefinido. (Se convierte en un colgando puntero después de la primera supr).

    {
        Foo* p = new Foo;
    
        Foo* q = p;
    
        //Has *q already been released?
        //(The answer is NO, but the runtime doesn't know that.)
    
        //(...suppose that pointees WOULD be automatically released...)
    
        //Has *p already been released?
        //(The answer WOULD now be YES, but the runtime doesn't know that.)
    }

    Sin inicializar punteros

    También es imposible detectar si un puntero a la variable ha sido inicializada a todos. Supongo que lo que sucede cuando intenta eliminar un puntero? Una vez más, la respuesta es un comportamiento indefinido.

        {
            Foo* p;
    
            //Has p been properly initialized?
            //(The answer is NO, but the runtime doesn't know that.)
        }

    Matrices dinámicas

    El tipo de sistema no distingue entre un puntero a un objeto único (Foo*) y un puntero al primer elemento de un array de objetos (también Foo*). Cuando una variable puntero es destruido, el tiempo de ejecución no es posible averiguar si se libera el pointee a través de delete o a través de delete[]. La liberación a través de la forma incorrecta invoca un comportamiento indefinido.

    {
        Foo* p = new Foo;
    
        Foo* q = new Foo[100];
    
        //What should I do, delete q or delete[] q?
        //(The answer is delete[] q, but the runtime doesn't know that.)
    
        //What should I do, delete p or delete[] p?
        //(The answer is delete p, but the runtime doesn't know that.)
    }

    Resumen

    Desde el tiempo de ejecución no puede hacer nada sensato con el pointee, la destrucción de una variable puntero es siempre un no-op. No hacer nada es definitivamente mejor que causan un comportamiento indefinido debido a un mal informado supongo 🙂

    Asesoramiento

    Lugar de raw punteros, considere el uso de punteros inteligentes como el tipo de valor de su contenedor, debido a que asuman la responsabilidad de la liberación de la pointee cuando ya no la necesite. Dependiendo de su necesidad, el uso std::shared_ptr<Foo> o std::unique_ptr<Foo> . Si el compilador no tienen soporte para C++0x, sin embargo, utilizar boost::shared_ptr<Foo>.

    Nunca, Repito, NUNCA uso std::auto_ptr<Foo> como el tipo de valor de un contenedor.

  2. 12

    Se llama al destructor de cada uno de los elementos en la list — pero eso no es un Node objeto. Su Node*.

    De modo que no se elimine el Node punteros.

    ¿ Que sentido?

    • Pero tengo una «Segmentación culpa’ si intento eliminar nodo después de quitar la llamada. : (¿Por qué sucedió?
    • Para eso, vamos a necesitar más de código.
    • Es claro que el objeto no se elimina-ed cuando sus primas-puntero se borra de un contenedor. Pero va a llamar a destruir-o del objeto? Creo que esto no debería ser el caso. Pero los médicos dicen que el destructor es llamado por el elemento del mapa que se va a eliminar.
  3. 7

    Se hace llamar el destructor de los datos en la lista. Eso significa que, std::list<T>::remove llamar al destructor de T (que es necesario que T es algo así como std::vector).

    En su caso, llamar al destructor de Node*, que es un no-op. No llamar al destructor de node.

    • Sí. Para mayor claridad, el código de ejemplo que se muestra no llame Node::~Node y no delete la node puntero. (Aunque si tenía una iterator a ese lugar en la lista, ahora es válido.)
  4. 3

    Sí, aunque en este caso, el Nodo* no tiene ningún destructor. Dependiendo de su funcionamiento interno, aunque, las diversas Nodo* los valores son borrados o destruidos por las reglas de ámbito. Si el Nodo* donde no sea de tipo fundamental, un destructor sería llamado.

    Es el destructor llamado en el Nodo? No, pero un «Nodo» no es el tipo de elemento en la lista.

    Cuanto a tu otra pregunta, no se puede. La lista estándar de contenedor (de hecho, TODOS los contenedores estándar) adoptar la titularidad de sus contenidos y de limpiar. Si usted no desea que esto suceda, el estándar de los contenedores no son una buena elección.

  5. 0

    Ya se están poniendo los punteros en un std::list, los destructores no se llama en la punta para Node objetos.

    Si desea almacenar el montón de objetos asignados en contenedores STL y hacer que se destruyan en el retiro, envuélvalos en un puntero inteligente como boost::shared_ptr

  6. 0

    La mejor manera de entender es a prueba de cada formulario y observe los resultados. Hábilmente utilizar el contenedor de objetos con sus propios objetos personalizados que usted necesita para tener una buena comprensión de la conducta.

    En definitiva, para el tipo de Node* ni el deconstructor se llama ni borrar/libre se invoca; sin embargo, para el tipo Node el deconstructor podría ser invocada mientras que la consideración de eliminar/free es un detalle de implementación de la lista. Es decir, depende de si la lista de implementación utilizada nuevo/malloc.

    En el caso de un unique_ptr<Node>, el deconstructor se invoca y el llamado de eliminar libre/va a suceder ya que había que darle algo asignados por new.

    #include <iostream>
    #include <list>
    #include <memory>
    using namespace std;
    void* operator new(size_t size) {
    cout << "new operator with size " << size << endl;
    return malloc(size);
    }
    void operator delete(void *ptr) {
    cout << "delete operator for " << ptr << endl;
    free(ptr);
    }
    class Apple {
    public:
    int id;
    Apple() : id(0) { cout << "apple " << this << ":" << this->id << " constructed" << endl; } 
    Apple(int id) : id(id) { cout << "apple " << this << ":" << this->id << " constructed" << endl; }
    ~Apple() { cout << "apple " << this << ":" << this->id << " deconstructed" << endl; }
    bool operator==(const Apple &right) {
    return this->id == right.id;
    }
    static void* operator new(size_t size) {
    cout << "new was called for Apple" << endl;
    return malloc(size);
    }
    static void operator delete(void *ptr) {
    cout << "delete was called for Apple" << endl;
    free(ptr);
    }
    /*
    The compiler generates one of these and simply assignments
    member variable. Think memcpy. It can be disabled by uncommenting
    the below requiring the usage of std::move or one can be implemented.
    */
    //Apple& operator=(const Apple &from) = delete;
    };
    int main() {
    list<Apple*> a = list<Apple*>();
    /* deconstructor not called */
    /* memory not released using delete */
    cout << "test 1" << endl;
    a.push_back(new Apple());
    a.pop_back();
    /* deconstructor not called */
    /* memory not released using delete */
    cout << "test 2" << endl;
    Apple *b = new Apple();
    a.push_back(b);
    a.remove(b);
    cout << "list size is now " << a.size() << endl;
    list<Apple> c = list<Apple>();      
    cout << "test 3" << endl;
    c.push_back(Apple(1)); /* deconstructed after copy by value (memcpy like) */
    c.push_back(Apple(2)); /* deconstructed after copy by value (memcpy like) */
    /*
    the list implementation will call new... but not
    call constructor when Apple(2) is pushed; however,
    delete will be called; since it was copied by value
    in the last push_back call
    double deconstructor on object with same data
    */
    c.pop_back();
    Apple z(10);
    /* will remove nothing */
    c.remove(z);
    cout << "test 4" << endl;
    /* Apple(5) will never deconstruct. It was literally overwritten by Apple(1). */
    /* Think memcpy... but not exactly. */
    z = Apple(1);
    /* will remove by matching using the operator== of Apple or default operator== */
    c.remove(z);
    cout << "test 5" << endl;
    list<unique_ptr<Apple>> d = list<unique_ptr<Apple>>();
    d.push_back(unique_ptr<Apple>(new Apple()));
    d.pop_back();
    /* z deconstructs */
    return 0;
    }

    De prestar atención a las direcciones de memoria. Usted puede decir que está señalando en la pila y que se apunta en el montón de los rangos.

Dejar respuesta

Please enter your comment!
Please enter your name here