c++ - delete - ¿Por qué necesito eliminar[]?
delete dynamic memory c++ (15)
¿Por qué necesitaría eliminar str si voy a finalizar el programa de todos modos?
Porque no quieres ser perezoso ...
No me importaría si ese recuerdo llega a una tierra llena de unicornios si solo voy a salir, ¿verdad?
No, tampoco me importa la tierra de los unicornios. La Tierra de Arwen es una cuestión diferente, entonces podríamos cortar sus cuernos y ponerlos a buen uso (he oído que es un buen afrodisíaco).
¿Es solo una buena práctica?
Es justamente una buena práctica.
¿Tiene consecuencias más profundas?
Alguien más tiene que limpiar después de ti. Tal vez te guste eso, me mudé del techo de mis padres hace muchos años.
Coloque una construcción de bucle while (1) alrededor de su código sin eliminar. La complejidad del código no importa. Las pérdidas de memoria están relacionadas con el tiempo de proceso.
Desde la perspectiva de la depuración, no liberar los recursos del sistema (manejadores de archivos, etc.) puede causar errores más significativos y difíciles de encontrar. Las fugas de memoria, aunque importantes, suelen ser mucho más fáciles de diagnosticar ( ¿por qué no puedo escribir en este archivo? ). El estilo incorrecto se convertirá en un problema mayor si comienza a trabajar con hilos.
int main()
{
while(1)
{
char* str = new char[10];
for(int i=0;i<5;i++)
{
//Do stuff with str
}
}
delete[] str;
return 0;
}
Digamos que tengo una función como esta:
int main()
{
char* str = new char[10];
for(int i=0;i<5;i++)
{
//Do stuff with str
}
delete[] str;
return 0;
}
¿Por qué necesitaría eliminar
str
si voy a finalizar el programa de todos modos? No me importaría si ese recuerdo llega a una tierra llena de unicornios si solo voy a salir, ¿verdad?¿Es solo una buena práctica?
¿Tiene consecuencias más profundas?
En lugar de hablar de este ejemplo específico, hablaré de casos generales, por lo que generalmente es importante llamar explícitamente a delete para desasignar la memoria porque (en el caso de C ++) puede tener algún código en el destructor que quiera ejecutar. Como escribir algunos datos en un archivo de registro o enviar una señal de apagado a otro proceso, etc. Si permite que el sistema operativo libere su memoria, su código en su destructor no se ejecutará.
Por otro lado, la mayoría de los sistemas operativos desasignarán la memoria cuando finalice su programa. Pero es una buena práctica desasignarlo usted mismo y, como he dado el ejemplo de destructor anterior, el sistema operativo no llamará a su destructor, lo que puede crear un comportamiento indeseable en ciertos casos.
Personalmente considero una mala práctica confiar en OS para liberar tu memoria (aunque lo hará), la razón es que si luego tienes que integrar tu código con un programa más grande, pasarás horas buscando y solucionando fugas de memoria.
¡Así que limpia tu habitación antes de irte!
Es una pregunta justa, y hay algunas cosas que considerar al responder:
- algunos objetos tienen destructores más complejos que no solo liberan memoria cuando se eliminan. Pueden tener otros efectos secundarios, que no desea omitir.
- La norma C ++ no garantiza que su memoria se liberará cuando finalice el proceso. (Por supuesto, en un sistema operativo moderno será liberado, pero si tuviera algún sistema operativo extraño que no lo hiciera, tendría que liberar su memoria correctamente
- por otro lado, ejecutar destructores en la salida del programa puede ocupar bastante tiempo, y si todo lo que se hace es liberar memoria (que se lanzaría de todos modos), entonces sí, tiene mucho sentido hacer un cortocircuito. eso y salir de inmediato en su lugar.
La mayoría de los sistemas operativos recuperarán memoria al salir del proceso. Las excepciones pueden incluir ciertos RTOS, dispositivos móviles antiguos, etc.
En un sentido absoluto, su aplicación no perderá memoria; Sin embargo, es una buena práctica limpiar la memoria que asigna, incluso si sabe que no causará una fuga real. Este problema es que las fugas son mucho, mucho más difíciles de solucionar que no tenerlas para empezar. Digamos que decides mover la funcionalidad en tu main () a otra función. Puede terminar con una fuga real.
También es mala estética, muchos desarrolladores mirarán la ''str'' no actualizada y sentirán náuseas leves :(
No puedo estar más de acuerdo con el excelente consejo de Eric Lippert:
Entonces la respuesta a la pregunta "¿debería liberar memoria antes de que mi programa salga?" es "depende de lo que hace tu programa".
Otras respuestas aquí han proporcionado argumentos a favor y en contra de ambos, pero el quid de la cuestión es lo que hace su programa . Considere un ejemplo más no trivial en el que la instancia de tipo que se asigna dinámicamente es una clase personalizada y el destructor de clases realiza algunas acciones que producen efectos secundarios . En tal situación, el argumento de la pérdida de memoria es trivial, el problema más importante es que si no se puede invocar delete
en dicha instancia de clase, se generará un comportamiento indefinido.
[basic.life] 3.8 Vida útil del objeto
Para 4:
Un programa puede finalizar la vida útil de cualquier objeto mediante la reutilización del almacenamiento que ocupa el objeto o llamando explícitamente al destructor para un objeto de un tipo de clase con un destructor no trivial. Para un objeto de un tipo de clase con un destructor no trivial, no es necesario que el programa llame al destructor explícitamente antes de que se reutilice o libere el almacenamiento que ocupa el objeto; sin embargo, si no hay una llamada explícita al destructor o si una expresión de eliminación (5.3.5) no se usa para liberar el almacenamiento, el destructor no se llamará implícitamente y cualquier programa que dependa de los efectos secundarios producidos por el destructor tiene un comportamiento indefinido
Así que la respuesta a su pregunta es como dice Eric "depende de lo que haga su programa"
Nota importante: la liberación de la memoria de delete
es casi un efecto secundario. Lo importante que hace es destruir el objeto. Con los diseños de RAII, esto podría significar cualquier cosa, desde el cierre de archivos, liberación de controladores del sistema operativo, terminación de hilos o eliminación de archivos temporales.
Algunas de estas acciones serían manejadas por el SO automáticamente cuando su proceso finaliza, pero no todas.
En su ejemplo, no hay razón para NO llamar a delete
. pero tampoco hay ninguna razón para llamar a los new
, por lo que puede eludir el problema de esta manera.
char str[10];
O bien, puede eludir la eliminación (y los problemas de seguridad de excepción involucrados) mediante el uso de punteros inteligentes ...
Por lo tanto, en general, siempre debe asegurarse de que la vida útil de su objeto se administre correctamente.
Pero no siempre es fácil: las soluciones para el fiasco de orden de inicialización estática a menudo significan que no tiene más remedio que confiar en que el sistema operativo limpie un puñado de objetos de tipo singleton para usted.
Otra razón que todavía no he mencionado es que la salida de las herramientas del analizador estático y dinámico (por ejemplo, valgrind o Coverity) sea más limpia y silenciosa. La salida limpia con cero pérdidas de memoria o cero problemas reportados significa que cuando aparece uno nuevo es más fácil de detectar y corregir.
Nunca se sabe cómo se usará o evolucionará su ejemplo simple. Es mejor comenzar tan limpio y fresco como sea posible.
Respuesta contraria: No, es una pérdida de tiempo. Un programa con una gran cantidad de datos asignados tendría que tocar casi cada página para devolver todas las asignaciones a la lista gratuita. Esto desperdicia tiempo de CPU, crea presión de memoria para datos poco interesantes y posiblemente incluso hace que el proceso intercambie páginas desde el disco. Simplemente salir libera toda la memoria al sistema operativo sin ninguna acción posterior.
(No es que esté en desacuerdo con las razones en "Sí", solo creo que hay argumentos en ambos sentidos)
Sí, es una buena práctica. NUNCA debe suponer que su sistema operativo se encargará de la desasignación de su memoria, si adquiere este hábito, le atormentará más adelante.
Sin embargo, para responder a su pregunta, al salir de la línea principal, el sistema operativo libera toda la memoria retenida por ese proceso, de modo que incluye cualquier conversación que pueda haber generado o variables asignadas. El sistema operativo se encargará de liberar esa memoria para que otros la usen.
Si de hecho su pregunta es "Tengo este programa trivial, ¿está bien que no libere algunos bytes antes de que salga?" la respuesta es sí, está bien. En cualquier sistema operativo moderno que va a estar bien. Y el programa es trivial; no es como si lo pusieras en un marcapasos o ejecutaras los sistemas de frenado de un Toyota Camry con esto. Si el único cliente es usted, entonces la única persona a la que posiblemente pueda impactar al ser descuidado es usted.
El problema luego aparece cuando comienzas a generalizar a casos no triviales a partir de la respuesta a esta pregunta sobre un caso trivial.
Entonces, hagamos dos preguntas sobre algunos casos no triviales.
Tengo un servicio de larga ejecución que asigna y desasigna la memoria de formas complejas, tal vez con múltiples asignadores que llegan a varios montones. Apagar mi servicio en el modo normal es un proceso complicado y lento que implica asegurarse de que el estado externo (archivos, bases de datos, etc.) se cierre sistemáticamente. ¿Debo asegurarme de que cada byte de memoria que asigné se desasigna antes de que se apague?
Sí, y te diré por qué. Una de las peores cosas que le puede pasar a un servicio de larga ejecución es si accidentalmente pierde la memoria. Incluso fugas pequeñas pueden sumar grandes fugas con el tiempo. Una técnica estándar para encontrar y reparar fugas de memoria es instrumentar los montones de asignación para que, al momento del cierre, registren todos los recursos que alguna vez se asignaron sin ser liberados. A menos que le guste perseguir una gran cantidad de falsos positivos y pasar mucho tiempo en el depurador, siempre libere su memoria, incluso si hacerlo no es estrictamente necesario.
El usuario ya está esperando que cerrar el servicio tarde en consumir miles de millones de nanosegundos, entonces, ¿a quién le importa si causa un poco de presión adicional sobre el asignador virtual y se asegura de que todo esté limpio? Este es solo el precio que paga por un gran software complicado. Y no es como si estuvieras cerrando el servicio todo el tiempo, así que de nuevo, ¿a quién le importa si es unos pocos milisegundos más lento de lo que podría ser?
Tengo el mismo servicio de larga duración. Si detecto que una de mis estructuras internas de datos está corrupta, deseo "fallar rápido". El programa está en un estado indefinido, es probable que se ejecute con privilegios elevados, y supongo que si detecto el estado dañado, es porque mi servicio está siendo atacado activamente por partes hostiles. Lo más seguro es cerrar el servicio de inmediato. Prefiero permitir que los atacantes nieguen el servicio a los clientes que arriesgar el servicio y mantener en peligro los datos de mis usuarios. En este escenario de cierre de emergencia, ¿debo asegurarme de que cada byte de memoria que asigné se libere?
Por supuesto no. El sistema operativo se encargará de eso por ti. Si tu pila está corrupta, los atacantes pueden esperar que liberes memoria como parte de su exploit. Cada milisegundo cuenta. ¿Y por qué te molestas en pulir los pomos de las puertas y limpiar la cocina antes de tirar un arma nuclear táctica en el edificio?
Entonces la respuesta a la pregunta "¿debería liberar memoria antes de que mi programa salga?" es "depende de lo que hace tu programa".
Sin mencionar que si vas a solicitar un trabajo como programador de C ++, hay muchas posibilidades de que no pases la entrevista debido a la eliminación que falta. Primero: a los programadores generalmente no les gustan las filtraciones (y el tipo en la entrevista seguramente será uno de ellos) y el segundo - la mayoría de las compañías (todas con las que trabajé al menos) tienen la política de "no fugas". En general, se supone que el software que escribe se ejecuta durante bastante tiempo, creando y destruyendo objetos sobre la marcha. En un entorno así, las fugas pueden provocar desastres ...
Su sistema operativo debe ocuparse de la memoria y limpiarla cuando salga de su programa, pero en general es una buena práctica liberar cualquier memoria que haya reservado. Creo que personalmente es mejor entrar en la mentalidad correcta de hacerlo, ya que mientras estás haciendo programas simples, lo más probable es que lo estés haciendo para aprender.
De cualquier forma, la única manera de garantizar que la memoria se libere es haciéndolo usted mismo.
Tienes muchas respuestas de experiencia profesional. Aquí estoy diciendo una ingenua pero una respuesta que considero como el hecho.
Resumen
3.
¿Tiene consecuencias más profundas?R: Responderá con cierto detalle.
2.
¿Es solo una buena práctica?R: Se considera una buena práctica. Libere los recursos / memoria que recuperó cuando esté seguro de que ya no los usa.
- ¿Por qué necesitaría eliminar
str
si voy a finalizar el programa de todos modos?
No me importaría si ese recuerdo llega a una tierra llena de unicornios si solo voy a salir, ¿verdad?
A: necesitas o no necesitas, de hecho, dices por qué. Hay alguna explicación a continuación.
Creo que eso depende Aquí hay algunas preguntas asumidas; el término programa puede significar una aplicación o una función.
P: ¿Depende de lo que haga el programa?
R: Si el universo destruido era aceptable, entonces no. Sin embargo, el programa podría no funcionar correctamente como se esperaba, e incluso podría ser un programa que no completa lo que se suponía. Es posible que desee pensar seriamente acerca de por qué construyes un programa como este?
P: ¿Depende de cómo el programa es complicado?
A: No. Ver explicación.
P: ¿Depende de qué se espera la estabilidad del programa?
A: De cerca.
Y considero que depende de
- ¿Cuál es el universo del programa?
- ¿Cuál es la expectativa del programa de que hizo su trabajo?
¿Cuánto le importa al programa a los demás y al universo en el que se encuentra?
Sobre el término universo , vea la Explicación.
Para resumen, depende de qué te importa.
- ¿Por qué necesitaría eliminar
Explicación
Importante: si definimos el término programa como una función, entonces su universo es aplicación . Hay muchos detalles omitidos; como idea para entender, es lo suficientemente largo, sin embargo.
Es posible que alguna vez hayamos visto este tipo de diagrama que ilustra la relación entre el software de aplicación y el software del sistema.
Pero por ser consciente del alcance del que cubre, sugiero un diseño invertido. Como solo hablamos de software, la capa de hardware se omite en el siguiente diagrama.
Con este diagrama, nos damos cuenta de que el sistema operativo cubre el mayor alcance, que es el universo actual, a veces decimos que el medio ambiente . Puede imaginarse que toda la arquitectura consiste en muchos discos como el diagrama, que puede ser un cilindro o un toro (una pelota es fina pero difícil de imaginar). Aquí debo mencionar que la mayoría de los sistemas operativos son de hecho unibody , el tiempo de ejecución puede ser simple o múltiple mediante implementaciones diferentes.
Es importante que el tiempo de ejecución sea responsable tanto del sistema operativo como de las aplicaciones, pero este último es más crítico. Runtime es el universo de las aplicaciones; si se destruye, todas las aplicaciones que se ejecutan debajo desaparecen.
A diferencia de los humanos en la Tierra, vivimos aquí, pero no estamos formados por la Tierra; todavía viviremos en otro entorno adecuado si el tiempo en que la Tierra está destruyendo y no estuviéramos allí.
Sin embargo, ya no podemos existir cuando se destruye el universo, porque no solo vivimos en el universo, sino que también lo hacemos en el universo.
Como se mencionó anteriormente, el tiempo de ejecución también es responsable para el sistema operativo. El círculo izquierdo en el siguiente diagrama es lo que parece.
Esto es principalmente como un programa C en el sistema operativo. Cuando la relación entre una aplicación y el sistema operativo coincide con esto, es simplemente la misma situación que el tiempo de ejecución en el sistema operativo anterior. En este diagrama, el sistema operativo es el universo de aplicaciones. La razón de las aplicaciones aquí debería ser responsable para el sistema operativo, es que el sistema operativo podría no virtualizar el código de ellas, o permitir que se bloquee. Si el sistema operativo siempre impide que lo hagan, entonces es responsable de sí mismo, independientemente de las aplicaciones. Pero piense en los controladores , es uno de los escenarios que el sistema operativo debe permitir que se bloquee, ya que este tipo de aplicaciones se tratan como parte del sistema operativo .
Finalmente, veamos el círculo correcto en el diagrama de arriba. En este caso, la aplicación en sí misma es el universo. A veces, llamamos a este tipo de sistema operativo de aplicación. Si un SO nunca permitió que se cargue y ejecute código personalizado, entonces hace todas las cosas por sí mismo. Incluso lo permitió, después de que se termina, la memoria no va a ninguna parte excepto el hardware . Toda la desasignación que puede ser necesaria, es antes de que se termine.
Entonces, ¿cuánto se preocupa su programa por los demás? ¿Cuánto le importa su universo? ¿Y cómo es la expectativa del programa de que hizo su trabajo? Depende de qué te importa .
new
y delete
son hermanos reservados palabra clave . Deben cooperar entre sí a través de un bloque de código o a través del ciclo de vida del objeto principal. Cada vez que el hermano menor comete un error ( new
), el hermano mayor querrá limpiarlo ( delete
). Entonces la madre (su programa) estará feliz y orgullosa de ellos.
TÉCNICAMENTE , un programador no debe confiar en el SO para hacer nada. El sistema operativo no es necesario para reclamar la memoria perdida de esta manera.
Si escribe el código que borra toda su memoria asignada dinámicamente, estará probando el código en el futuro y permitiendo que otros lo utilicen en un proyecto más grande.
Fuente: Asignación y Mitos GC (¡Alerta de PostScript!)
Allocation Myth 4: Non-garbage-collected programs should always deallocate all memory they allocate. The Truth: Omitted deallocations in frequently executed code cause growing leaks. They are rarely acceptable. but Programs that retain most allocated memory until program exit often perform better without any intervening deallocation. Malloc is much easier to implement if there is no free. In most cases, deallocating memory just before program exit is pointless. The OS will reclaim it anyway. Free will touch and page in the dead objects; the OS won''t. Consequence: Be careful with "leak detectors" that count allocations. Some "leaks" are good!
Creo que es una práctica muy mala usar malloc / new sin llamar a free / delete.
Si la memoria va a recuperarse de todos modos, ¿qué daño puede haber desde la desasignación explícita cuando lo necesites?
Tal vez si el sistema operativo "recupera" la memoria más rápido que lo hace gratis, verá un aumento en el rendimiento; esta técnica no lo ayudará con ningún programa que deba seguir funcionando durante un período de tiempo prolongado.
Habiendo dicho eso, te recomiendo que uses free / delete.
Si te metes en este hábito, ¿quién puede decir que un día no aplicarás accidentalmente este enfoque en algún lugar importante?
Uno siempre debe desasignar los recursos después de que uno haya terminado con ellos, ya sea manejadores de archivos / memoria / mutex. Al tener ese hábito, uno no cometerá ese tipo de error al construir servidores. Se espera que algunos servidores funcionen 24x7. En esos casos, cualquier fuga de cualquier tipo significa que su servidor eventualmente se quedará sin ese recurso y se bloqueará / bloqueará de alguna manera. Un programa de utilidad corto, ya que una fuga no es tan mala. Cualquier servidor, cualquier fuga es muerte. Hazte un favor. Limpia después de ti mismo. Es un buen hábito.
Think about your class ''A'' having to deconstruct. If you don''t call ''delete'' on ''a'', that destructor won''t get called. Usually, that won''t really matter if the process ends anyway. But what if the destructor has to release e.g. objects in a database? Flush a cache to a logfile? Write a memory cache back to disk? **You see, it''s not just ''good practice'' to delete objects, in some situations it is required**.