2010-06-25 11 views
9

Una es usar excepciones de C++: intente capturar bloques. Pero liberar la memoria dinámica será un problema cuando se presente una excepción.Cuál es el enfoque correcto para el manejo de errores en C++

segundo es utilizar el estilo C: variable errno

En tercer lugar es sólo para volver -1 en caso de error y 0 en caso de éxito :)

¿Qué camino debe ser elegido para un proyecto de tamaño medio y por qué? Cualquier otro enfoque mejor ...?

+8

Utilice la excepción para ERRORES EXCEPCIONALES (http://stackoverflow.com/questions/180937/are-exceptions-really-for-exceptional-errors). Para errores comunes y recurrentes, puede probar las nuevas instalaciones de system_error en C++ 0x http://blog.think-async.com/2010/04/system-error-support-in-c0x-part-1.html. Hay una implementación de impulso. – anno

+0

Deberías usar ambos. Cuál depende de la situación. ver http://stackoverflow.com/questions/106586/what-are-the-principles-guiding-your-exception-handling-policy/106749#106749 –

+0

anno: El enlace al que hace referencia parece estar en desacuerdo con la palabra " excepcional "- que tiene mucho sentido para mí. Después de todo, no puedes predecir cuáles serán los errores comunes. – Ken

Respuesta

28

Pero liberar la memoria dinámica será un problema cuando se produzca una excepción.

No, no lo es. std::vector<int> v(100); Hecho.

El concepto aquí se llama Scope-Bound Resource Management (SBRM), también conocido por el nombre mucho más común (e incómodo) Adquisición de recursos Inicialización (RAII). Básicamente, todos los recursos están contenidos en algún objeto que limpiará el recurso en el destructor (que siempre se garantiza que se ejecutará para un objeto asignado automáticamente). Entonces, ya sea que la función exista normalmente o por excepción, se ejecuta el destructor y se limpia su recurso.

Nunca haga una asignación donde necesite liberarla explícitamente, use contenedores y punteros inteligentes.

+1

De acuerdo en general, aunque evitaría usar "nunca" en una directriz como esta - para cada regla, hay excepciones. La experiencia consiste en aprender dónde tienen sentido esas excepciones. – Stabledog

+3

@Stable: "para cada regla, hay excepciones", ¿es eso una regla? En cualquier caso, además de escribir el contenedor de recursos en sí, * nunca * tendrá que no usar el contenedor. Nunca tiene sentido ponerse en una posición de tener que tener mucho cuidado al recordar eliminar algo, siempre se puede automatizar en un destructor. – GManNickG

+0

Hay muy pocas excepciones si usa C++ cuando se trata de RAII. Todos los recursos asignados en una clase deben liberarse para cuando se destruya la instancia de la clase. Todas las asignaciones y desasignaciones de memoria deben ocurrir dentro de las clases, con la excepción de los asignadores de memoria que aún se deben usar para asignar y desasignar memoria dentro de una clase. Para la seguridad de excepciones, RAII no debe considerarse una opción; es obligatorio. – stinky472

4

segundo es utilizar el estilo C: variable errno

En tercer lugar es sólo para volver -1 en caso de error y 0 en caso de éxito :)

Y ¿Cómo ayudan a resolver el problema de la liberación memoria dinámica? También usan una estrategia de salida anticipada, igual que throw.

Por lo tanto, en resumen, no tienen una ventaja sobre las excepciones de C++ (según usted).

+0

¿Cómo se cambia el valor que se devuelve mandato early-exit? 'int func() {int returnValue = 0; ... (puede establecer returnValue a otros valores); return returnValue; } ' – Bill

+0

@Bill: bueno, eso está usando la convención de punto único de salida que está desactualizada, y por una buena razón. En realidad, no hace que el control fluya mejor. Y también se puede aplicar con excepciones de C++, por supuesto. No hay diferencia, realmente. Solo una variable adicional necesaria para almacenar en caché la excepción para lanzar. –

1

Lance una excepción. Los destructores de variables siempre se invocan cuando se lanza una excepción, y si las variables basadas en la pila no se limpian (si, por ejemplo, usaste un puntero sin formato cuando necesitas eliminar el resultado), obtienes lo que mereces. . Use punteros inteligentes, sin pérdidas de memoria.

2

Echa un vistazo a este comentario de Herb Sutter sobre try catch para C++ GOTW. Y revisa todo su conjunto de artículos. Él tiene mucho que decir sobre cuándo y cómo verificar y salvarse a sí mismo de las condiciones de error y cómo manejarlas de la mejor manera posible.

3

En primer lugar, debe esforzarse por un programa con un mínimo de errores. (Debido a que los errores no son buenos)

Las excepciones son una buena herramienta, pero should be used conservatively: reservarlas para "casos excepcionales", no las use para controlar el flujo de su programa.

Por ejemplo, no use excepciones para comprobar si la entrada del usuario es correcta o no. (En tal caso, devuelva un código de error.)

1

Pero liberar la memoria dinámica será un problema cuando se produzca una excepción.

La memoria de liberación (o cualquier otro recurso para el caso) no se convierte repentinamente en un problema porque no utiliza excepciones. Las técnicas que hacen que lidiar con estos problemas, mientras que las excepciones pueden ser fáciles, también lo hacen más fácil cuando puede haber "condiciones de error".

1

Las excepciones son buenas para pasar el control de un contexto a otro.
Deje que el compilador haga el trabajo de desenrollar la pila entre los contextos y luego, en el nuevo contexto, compense la excepción (y esperemos que continúe).

Si ocurre su error y puede corregirse en el mismo contexto, los códigos de error son un buen método para gestionar errores y limpiarlos (no interprete esto como que no debería usar RAII, todavía lo necesita). Pero, por ejemplo, dentro de una clase se produce un error en una función y la función de llamada puede corregir ese tipo de error (entonces probablemente no sea una circunstancia excepcional, por lo que no hay excepciones), entonces el código de error es útil.

No debe usar códigos de error cuando tenga que pasar información desde una biblioteca o subsistema, ya que entonces confía en que el desarrollador use el código para verificar y manejar el código para asegurarse de que funciona correctamente y con mayor frecuencia. que no ignorarán los códigos de error.

3

Una es usar excepciones de C++: intente atrapar bloques. Pero liberar la memoria dinámica será un problema cuando se produce una excepción .

@see RAII.

Las excepciones deben ser su método preferido para hacer frente a situaciones excepcionales de tiempo de ejecución, como quedarse sin memoria. Tenga en cuenta que algo como std :: map :: find no arroja (y no debería) porque no es necesariamente un error o un caso particularmente excepcional buscar una clave que no existe: la función puede informar al cliente si o no, la clave existe. No es como una violación de una precondición o condición posterior como requerir que exista un archivo para que un programa funcione correctamente y encontrar que el archivo no está allí.

La belleza del manejo de excepciones, si lo hace correctamente (de nuevo, @see RAII), es que evita la necesidad de ensuciar el código de manejo de errores en su sistema.

Consideremos un caso en el que la función A llama a la función B que llama a C, luego a D y así sucesivamente hasta llegar a 'Z'. Z es la única función que puede arrojar, y A es el único interesado en recuperarse de un error (A es el punto de entrada para una operación de alto nivel, por ejemplo, como cargar una imagen). Si se apega a RAII que será útil para algo más que el manejo de excepciones, entonces solo necesita poner una línea de código en Z para lanzar una excepción y un pequeño bloque try/catch en A para capturar la excepción y, por ejemplo, mostrar un mensaje de error para el usuario.

Desafortunadamente, mucha gente no se adhiere a RAII tan estrictamente como debería hacerlo en la práctica, por lo que muchos códigos del mundo real tienen más bloqueos try/catch que los necesarios para lidiar con la limpieza manual de recursos (que no debería debe ser manual). Sin embargo, este es el ideal que debe esforzarse por lograr en su código, y es más práctico si se trata de un proyecto de tamaño medio. Del mismo modo, en escenarios del mundo real, las personas a menudo ignoran los códigos de error devueltos por las funciones. Si va a hacer un esfuerzo adicional a favor de la robustez, también podría comenzar con RAII porque eso ayudará a su aplicación, independientemente de si utiliza el manejo de excepciones o el manejo de códigos de error.

Hay una advertencia: no debe arrojar excepciones a través de los límites del módulo.Si lo hace, debe considerar un híbrido entre los códigos de error (como en devolver códigos de error, no usar un estado de error global como errno) y excepciones.

Vale la pena señalar que si se utiliza el operador nuevo en su código sin especificar nothrow en todas partes, por ejemplo:

int* p = new int(123); // can throw std::bad_alloc 
int* p = new(std::nothrow) int(123); // returns a null pointer on failure 

... entonces ya necesario capturar y manejar excepciones bad_alloc en su código para que se ser robusto contra excepciones de falta de memoria.

Cuestiones relacionadas