c++ coding-style assert

Está utilizando assert() en C++ mala práctica?



coding-style (5)

Tiendo a agregar muchas aserciones a mi código C ++ para facilitar la depuración sin afectar el rendimiento de las compilaciones de lanzamiento. Ahora, assert es una macro C pura diseñada sin mecanismos de C ++ en mente.

C ++ por otro lado define std::logic_error , que se debe lanzar en los casos en que hay un error en la lógica del programa (de ahí el nombre). Lanzar una instancia puede ser la alternativa perfecta, más C ++ ish para assert .

El problema es que assert y abort terminan el programa inmediatamente sin llamar a los destructores, omitiendo la limpieza, mientras que lanzar una excepción manualmente agrega costos de tiempo de ejecución innecesarios. Una forma de evitar esto sería crear una macro de afirmación propia SAFE_ASSERT , que funciona igual que la contraparte C, pero arroja una excepción en caso de error.

Puedo pensar en tres opiniones sobre este problema:

  • Mantener la afirmación de C Como el programa finaliza inmediatamente, no importa si los cambios se desenrollan correctamente. Además, usar #define s en C ++ es igual de malo.
  • Lanza una excepción y captúrala en main () . Permitir que el código omita los destructores en cualquier estado del programa es una mala práctica y se debe evitar a toda costa, al igual que las llamadas a terminar (). Si se lanzan excepciones, deben ser atrapadas.
  • Lanza una excepción y deja que termine el programa. Una excepción que termina un programa está bien, y debido a NDEBUG , esto nunca sucederá en una compilación de lanzamiento. La captura no es necesaria y expone los detalles de implementación del código interno a main() .

¿Hay una respuesta definitiva a este problema? ¿Alguna referencia profesional?

Editado: Skipping Destructores es, por supuesto, un comportamiento indefinido.


En mi humilde opinión, las afirmaciones son para controlar las condiciones que, si se violan, hacen que todo lo demás carezca de sentido. Y, por lo tanto, no puede recuperarse de ellos o, mejor dicho, la recuperación es irrelevante.

Yo los agruparía en 2 categorías:

  • Pecados del desarrollador (por ejemplo, una función de probabilidad que arroja valores negativos):

probabilidad de flotación () {retorno -1.0; }

assert (probabilidad ()> 0.0)

  • La máquina está rota (por ejemplo, la máquina que ejecuta su programa está muy equivocada):

int x = 1;

assert (x> 0);

Estos son ejemplos triviales pero no muy alejados de la realidad. Por ejemplo, piense en algoritmos ingenuos que devuelven índices negativos para usar con vectores. O programas integrados en hardware personalizado. O más bien porque la mierda sucede .

Y si existen tales errores de desarrollo, no debe confiar en ningún mecanismo de recuperación o manejo de errores implementado. Lo mismo aplica para los errores de hardware.


Las afirmaciones son completamente apropiadas en el código C ++. Las excepciones y otros mecanismos de manejo de errores en realidad no están destinados para lo mismo que las aserciones.

El manejo de errores es para cuando existe la posibilidad de recuperar o informar un error al usuario. Por ejemplo, si hay un error al intentar leer un archivo de entrada, es posible que desee hacer algo al respecto. Los errores pueden ser el resultado de errores, pero también pueden ser simplemente el resultado apropiado para una entrada determinada.

Las afirmaciones son para cosas como comprobar que se cumplen los requisitos de una API cuando normalmente no se verifica la API, o para verificar cosas que el desarrollador cree que está garantizado por la construcción. Por ejemplo, si un algoritmo requiere una entrada ordenada, normalmente no lo verificaría, pero podría tener una aserción para verificarlo, de modo que las depuraciones generen ese tipo de error. Una afirmación siempre debe indicar un programa que funciona incorrectamente.

Si está escribiendo un programa donde un cierre sucio podría causar un problema, entonces puede evitar las afirmaciones. El comportamiento no definido estrictamente en términos del lenguaje C ++ no califica como tal problema aquí, ya que tocar una afirmación probablemente ya sea el resultado de un comportamiento indefinido o la violación de algún otro requisito que podría evitar que alguna limpieza funcione correctamente.

Además, si implementa aserciones en términos de una excepción, podría ser atrapada y "manipulada", aunque esto contradiga el propósito de la afirmación.


Las aserciones se pueden usar para verificar las invariantes de implementación interna, como el estado interno antes o después de la ejecución de algún método, etc. Si la aserción falla, realmente significa que la lógica del programa está rota y no puede recuperarse de esto. En este caso, lo mejor que puede hacer es romper lo antes posible sin pasar una excepción al usuario. Lo que es realmente agradable acerca de las afirmaciones (al menos en Linux) es que el volcado del núcleo se genera como resultado de la terminación del proceso y, por lo tanto, puede investigar fácilmente el seguimiento y las variables de la pila. Esto es mucho más útil para comprender la falla lógica que el mensaje de excepción.


No ejecutar destructores debido a alling abort () no es un comportamiento indefinido!

Si lo fuera, entonces sería un comportamiento indefinido llamar también a std::terminate() , ¿cuál sería el sentido de proporcionarlo?

assert() es tan útil en C ++ como en C. Las afirmaciones no son para el manejo de errores, sino para abortar el programa inmediatamente.


  • Las afirmaciones son para la depuración . El usuario de su código enviado nunca debería verlos. Si se golpea una afirmación, debe corregirse su código.

  • Las excepciones son por circunstancias excepcionales . Si se encuentra uno, el usuario no podrá hacer lo que quiera, pero puede continuar en otro lugar.

  • El manejo de errores es para el flujo normal del programa. Por ejemplo, si indicas al usuario un número y obtienes algo inmanejable, eso es normal , porque la entrada del usuario no está bajo tu control y siempre debes manejar todas las situaciones posibles por rutina. (Por ejemplo, bucle hasta que tenga una entrada válida, diciendo "Lo siento, inténtelo de nuevo").