usar resueltos memoria manejo ejercicios ejemplos ejemplo dinamicos dinamica como asignacion arreglos c++ placement-new

c++ - resueltos - ¿Cómo liberar adecuadamente la memoria asignada por la nueva ubicación?



memoria malloc (4)

He estado leyendo que cuando utilizas la ubicación nueva , tienes que llamar al destructor manualmente.

Considere el siguiente código:

// Allocate memory ourself char* pMemory = new char[ sizeof(MyClass)]; // Construct the object ourself MyClass* pMyClass = new( pMemory ) MyClass(); // The destruction of object is our duty. pMyClass->~MyClass();

Por lo que sé, el operador delete normalmente llama al destructor y luego desasigna la memoria, ¿verdad? Entonces, ¿por qué no usamos delete lugar?

delete pMyClass; //what''s wrong with that?

en el primer caso, nos vemos obligados a establecer pMyClass en nullptr después de llamar a destructor así:

pMyClass->~MyClass(); pMyClass = nullptr; // is that correct?

PERO el destructor NO desasignó la memoria, ¿verdad? Entonces, ¿sería eso una pérdida de memoria?

Estoy confundido, ¿puedes explicar eso?


El uso del new operador hace dos cosas, llama al operator new función operator new que asigna memoria y luego utiliza la ubicación nueva para crear el objeto en esa memoria. El operador de delete llama al destructor del objeto y luego llama a operator delete . Sí, los nombres son confusos.

//normal version calls these two functions MyClass* pMemory = new MyClass; void* pMemory = operator new(sizeof(MyClass)); MyClass* pMyClass = new( pMemory ) MyClass(); //normal version calls these two functions delete pMemory; pMyClass->~MyClass(); operator delete(pMemory);

Como en su caso, utilizó la ubicación nueva manualmente, también necesita llamar al destructor manualmente. Como asignó la memoria manualmente, debe liberarla manualmente. Recuerde, si asigna con new , debe haber una delete . Siempre.

Sin embargo, la ubicación nueva está diseñada para funcionar también con búferes internos (y otros escenarios), donde los búferes no fueron asignados con el operator new , por lo que no debe llamar al operator delete .

#include <type_traits> struct buffer_struct { std::aligned_storage<sizeof(MyClass)>::type buffer; }; int main() { buffer_struct a; MyClass* pMyClass = new (&a.buffer) MyClass(); //created inside buffer_struct a //stuff pMyClass->~MyClass(); //can''t use delete, because there''s no `new`. return 0; }

El propósito de la clase buffer_struct es crear y destruir el almacenamiento de cualquier manera, mientras que main se encarga de la construcción / destrucción de MyClass , tenga en cuenta que los dos están (casi *) completamente separados entre sí.

* tenemos que estar seguros de que el almacenamiento tiene que ser lo suficientemente grande


Es necesario distinguir entre el operador delete y el operator delete . En particular, si está utilizando una ubicación nueva, invoca explícitamente el destructor y luego llama al operator delete (y no al operador delete ) para liberar la memoria, es decir

X *x = static_cast<X*>(::operator new(sizeof(X))); new(x) X; x->~X(); ::operator delete(x);

Tenga en cuenta que esto utiliza el operator delete , que es de un nivel inferior al del operador delete y no se preocupa por los destructores (es esencialmente un poco como free ). Compare esto con el operador de delete , que internamente hace el equivalente de invocar el destructor y llamar al operator delete .

Vale la pena señalar que no tiene que usar ::operator new y ::operator delete para asignar y desasignar su búfer - en lo que respecta a la nueva ubicación, no importa cómo se desata / destruye el búfer. El punto principal es separar las preocupaciones de la asignación de memoria y el tiempo de vida del objeto.

Incidentalmente, una posible aplicación de esto sería algo así como un juego, donde es posible que desee asignar un gran bloque de memoria por adelantado para poder administrar con cuidado su uso de memoria. Luego construirías objetos en la memoria que ya has adquirido.

Otro uso posible sería en un asignador de objetos pequeño, de tamaño fijo optimizado.


Probablemente sea más fácil de entender si te imaginas construyendo varios objetos MyClass dentro de un bloque de memoria.

En ese caso, iría algo así como:

  1. Asigne un bloque de memoria gigante utilizando el nuevo char [10 * sizeof (MyClass)] o malloc (10 * sizeof (MyClass))
  2. Use la ubicación nueva para construir diez objetos MyClass dentro de esa memoria.
  3. Hacer algo.
  4. Llama al destructor de cada uno de tus objetos.
  5. Desasigne el bloque grande de memoria usando delete [] o free ().

Este es el tipo de cosas que podría hacer si está escribiendo un compilador, un sistema operativo, etc.

En este caso, espero que quede claro por qué necesita pasos separados de "destructor" y "eliminar", porque no hay razón para llamar a eliminar. Sin embargo, debe desasignar la memoria como lo haría normalmente (libre, eliminar, no hacer nada por una matriz estática gigante, salir normalmente si la matriz es parte de otro objeto, etc.) y si no lo hace, se filtrará

También tenga en cuenta que, como dijo Greg, en este caso, no puede usar eliminar, porque asignó la matriz con nuevo [], por lo que tendría que usar eliminar [].

También tenga en cuenta que debe asumir que no ha anulado la eliminación de MyClass, de lo contrario, hará algo totalmente diferente que es casi seguro que es incompatible con "nuevo".

Así que creo que es improbable que quieras llamar "eliminar" como lo describiste, pero ¿podría alguna vez funcionar? Creo que esta es básicamente la misma pregunta que "Tengo dos tipos no relacionados que no tienen destructores. ¿Puedo crear un puntero a un tipo y luego borrar esa memoria a través de un puntero a otro tipo?" En otras palabras, "mi compilador tiene una lista grande de todas las cosas asignadas, o puede hacer cosas diferentes para diferentes tipos".

Me temo que no estoy seguro. Leyendo la especificación que dice:

5.3.5 ... Si el tipo estático del operando [del operador de eliminación] es diferente de su tipo dinámico, el tipo estático será una clase base del tipo dinámico del operando y el tipo estático tendrá un destructor virtual o El comportamiento no está definido.

Creo que eso significa "Si usas dos tipos no relacionados, no funciona (está bien eliminar un objeto de clase polimórficamente a través de un destructor virtual)".

Así que no, no hagas eso. Sospecho que a menudo puede funcionar en la práctica, si el compilador mira únicamente la dirección y no el tipo (y ninguno de los tipos es una clase de herencia múltiple, lo que dañaría la dirección), pero no lo intente.


Una razón por la que esto está mal:

delete pMyClass;

es que debe eliminar pMemory con delete[] ya que es una matriz:

delete[] pMemory;

No puedes hacer las dos cosas anteriores.

De manera similar, puede preguntar por qué no puede usar malloc() para asignar memoria, la ubicación nueva para construir un objeto y luego delete para eliminar y liberar la memoria. La razón es que debe coincidir con malloc() y free() , no malloc() y delete .

En el mundo real, casi nunca se usan llamadas de destructoras nuevas y explícitas. Pueden ser utilizados internamente por la implementación de la biblioteca estándar (o para otra programación a nivel de sistema como se indica en los comentarios), pero los programadores normales no los usan. Nunca he usado tales trucos para el código de producción en muchos años de hacer C ++.