2011-09-01 5 views
37

Hice algunas investigaciones después de aprender new, a diferencia de malloc() al cual estoy acostumbrado, no devuelve NULL para las asignaciones fallidas, y encontré que hay dos formas distintas de verificar si el nuevo tuvo éxito o no. Estas dos formas son:Nuevo (std :: nothrow) vs. Nuevo dentro de un bloque try/catch

try 
{ 
    ptr = new int[1024]; 
} 
catch(std::bad_alloc& exc) 
{ 
    assert(); 
}; 

y

ptr = new (std::nothrow) int[1024]; 
if(ptr == NULL) 
    assert(); 

Creo que las dos formas de lograr el mismo objetivo, (! Corrígeme si estoy equivocado, por supuesto), por lo que mi pregunta es la siguiente:

que es la mejor opción para comprobar si new tuvo éxito, basado enteramente en la legibilidad, la mantenibilidad y el rendimiento, sin tener en cuenta la convención de programación de C++ de facto.

+2

Use excepciones. Hacen que su código sea más significativo, local, sostenible y escalable. –

+3

Respecto a la legibilidad solamente, definitivamente el que sin excepción maneja. En cuanto al rendimiento, tal vez también el 'nohrow'. Pero estoy seguro de que los ex-excepcionistas discutirán. Pero si solo quiere una falla de aserción simple, también puede usar la variante de lanzamiento y omitir el manejador de excepciones;) –

+4

¿Cuál es el objetivo de ese código? Estás atrapando una excepción específica y significativa y luego creando una sin sentido a través de 'assert()' ... – Blindy

Respuesta

47

Considera lo que estás haciendo. Estás asignando memoria. Y si por alguna razón la asignación de memoria no puede funcionar, usted assert. Que es más o menos exactamente lo que sucederá si solo dejas que std::bad_alloc se propague de nuevo a main. En una compilación de lanzamiento, donde assert no funciona, su programa se bloqueará cuando intente acceder a la memoria. Por lo tanto, es lo mismo que dejar que surja la excepción: detener la aplicación.

Así que hágase la pregunta: ¿Tiene realmente que le importa lo que sucede si se queda sin memoria? Si todo lo que hace es afirmar, entonces el método de excepción es mejor, porque no satura su código con assert s al azar. Simplemente deja que la excepción vuelva al main.

Si de hecho tiene una ruta de código especial en el caso de que no pueda asignar memoria (es decir, puede continuar funcionando), las excepciones pueden ser o no un camino a seguir, dependiendo de qué es la ruta de código . Si la ruta de código es solo un interruptor configurado al tener un puntero nulo, entonces la versión nothrow será más simple.Si, en cambio, necesita hacer algo bastante diferente (extraer de un búfer estático, o eliminar algunas cosas, o lo que sea), entonces capturar std::bad_alloc es bastante bueno.

+3

Este es un argumento bastante convincente, no sabía que las excepciones podrían subir la pila de llamadas para encontrar un bloque catch. ... No estoy al tanto de muchas cosas que las excepciones pueden hacer. Puede que sea hora de que finalmente lo aprenda después de todos estos años, aha –

10

Depende del contexto donde se realiza la asignación. Si su programa puede continuar incluso si la asignación falla (quizás devuelva un código de error a la persona que llama), utilice el método std::nothrow y verifique NULL. De lo contrario, estaría utilizando excepciones para el flujo de control, lo cual no es una buena práctica.

Por otro lado, si su programa necesita tener esa memoria asignada correctamente para poder funcionar, use try-catch para capturar (no necesariamente en las inmediaciones del new) una excepción y salir con gracia de la programa.

+0

No lo consideré, supongo que mezclar y combinar para la situación no sería un mal estilo en ese caso. Obviamente las dos formas existen por esa razón. ¡Gracias! –

+4

Esa es una buena respuesta. Resume toda la filosofía de las excepciones: use excepciones para situaciones excepcionales y tómelas donde puedan manejarse de manera significativa. Por otro lado, para una situación de tiempo de ejecución no excepcional que puede y desea tratar localmente, no use excepciones. –

6

Desde una perspectiva de rendimiento puro, importa poco. Hay una sobrecarga inherente con el manejo de excepciones, aunque esta sobrecarga generalmente vale la pena compensar en la legibilidad y el mantenimiento de la aplicación. Las fallas de asignación de memoria de esta naturaleza no deberían estar en el caso del 99% de su aplicación, por lo que esto debería ocurrir con poca frecuencia.

Desde un punto de vista del rendimiento, generalmente quiere evitar el asignador estándar debido a su rendimiento relativamente bajo de todos modos.

Dicho esto, generalmente acepto la versión arrojada de excepción porque generalmente nuestras aplicaciones están en un estado donde si la asignación de memoria falla, hay poco que podamos hacer aparte de salir graciosamente con un mensaje de error apropiado, y guardamos el rendimiento no requiere NULL comprobando nuestros recursos asignados recientemente porque, por definición, una falla de asignación moverá el alcance desde donde eso importe.

+0

¿Entonces la penalidad de rendimiento de las excepciones sería menor que la comprobación de igualdad de punteros, suponiendo que nada va mal? Si ese es el caso, no veo ninguna razón para no usar excepciones ahora. (Y sí, trato de ajustar todas mis necesidades de asignación a un pequeño asignador de objetos, guardo todas las cosas de la memoria en un lugar ordenado también) –

+0

Todavía hay alguna penalización al usar excepciones (el idioma lo hace por usted), como la configuración subir la pila de forma adecuada, el procesamiento de SEH, etc. En general, no esperaría que haya una diferencia medible entre los dos en un caso en el que "nada sale mal". En caso de falla, el código de excepción será mucho más complicado, pero limpia el código al no requerir las verificaciones 'NULL'. Sin embargo, en realidad es una cuestión de preferencia personal en ese momento. – Chad

+2

En la implementación de GCC, las excepciones no le cuestan nada (aparte de una localidad de memoria ligeramente peor) si no se usan, solo paga cuando se lanza la excepción. –

-3

new se utiliza para crear objetos, no para asignar memoria, por lo tanto, su ejemplo es algo artificial.

Los constructores de objetos suelen lanzar si fallan. Después de haber recorrido la implementación new en Visual Studio más de unas pocas veces, no creo que el código capte ninguna excepción. Por lo tanto, generalmente tiene sentido buscar excepciones al crear objetos.

I thinkstd::bad_alloc se lanza solo si falla la parte de asignación de memoria. No estoy seguro de qué pasará si pasa std::nothrow a new, pero el constructor del objeto arroja - hay ambigüedad en los documentos que he leído.

La diferencia de rendimiento entre los 2 enfoques es probablemente irrelevante, ya que la mayor parte del tiempo del procesador se puede gastar fácilmente en el constructor del objeto o buscando en el montón.

Una regla de oro no siempre es apropiada. Por ejemplo, los sistemas en tiempo real suelen restringir las asignaciones de memoria dinámica, por lo que new, si está presente, probablemente estaría sobrecargado. En ese caso, podría hacer uso de un puntero nulo devuelto y manejar el fallo localmente.

+0

Si su objeto principal tiene subobjetos, entonces también pueden lanzar bad_alloc si asignan memoria ... por lo que no sabrá con certeza si es la asignación principal que tiene ha fallado. Sin embargo, no le importa ya que el objeto no se puede usar de todos modos y el compilador se encargará de realizar la limpieza requerida de los objetos parcialmente construidos. ** Le sugiero que aprenda primero el idioma ** antes de generar respuestas arbitrarias que sean engañosas. En particular, su recomendación de buscar excepciones al crear objetos es mala si significa poner a prueba/capturar cada asignación. – Phil1970

+0

Te recomiendo que leas ** C++ Excepcional ** y otros libros similares antes de escribir cualquier código usado en aplicaciones profesionales. – Phil1970

Cuestiones relacionadas