c++ - usar - Captura de excepción: dividir por cero
try catch ejemplo (7)
El siguiente código no capta una excepción cuando trato de dividir por 0. ¿Tengo que lanzar una excepción, o la computadora lanza automáticamente una en el tiempo de ejecución?
int i = 0;
cin >> i; // what if someone enters zero?
try {
i = 5/i;
}
catch (std::logic_error e) {
cerr << e.what();
}
Debe lanzar la excepción manualmente usando throw
keyword.
Ejemplo:
#include <iostream>
using namespace std;
double division(int a, int b)
{
if( b == 0 )
{
throw "Division by zero condition!";
}
return (a/b);
}
int main ()
{
int x = 50;
int y = 0;
double z = 0;
try {
z = division(x, y);
cout << z << endl;
}catch (const char* msg) {
cerr << msg << endl;
}
return 0;
}
Debe verificarlo usted mismo y lanzar una excepción. La división de enteros por cero no es una excepción en C ++ estándar.
Tampoco el punto flotante se divide por cero, pero al menos eso tiene medios específicos para manejarlo.
Las excepciones enumeradas en el estándar ISO son:
namespace std {
class logic_error;
class domain_error;
class invalid_argument;
class length_error;
class out_of_range;
class runtime_error;
class range_error;
class overflow_error;
class underflow_error;
}
y pensarías que overflow_error
sería ideal para indicar una división por cero.
Pero la sección 5.6
(de C++11
, aunque no creo que esto haya cambiado respecto a la iteración anterior) establece específicamente:
Si el segundo operando de
/
o%
es cero, el comportamiento no está definido.
Por lo tanto, podría arrojar esa (o cualquier otra) excepción. También podría formatear su disco duro y reírse burlonamente :-)
Si desea implementar tal bestia, podría usar algo como intDivEx
en el siguiente programa:
#include <iostream>
#include <stdexcept>
// Integer division, catching divide by zero.
inline int intDivEx (int numerator, int denominator) {
if (denominator == 0)
throw std::overflow_error("Divide by zero exception");
return numerator / denominator;
}
int main (void) {
int i = 42;
try {
i = intDivEx (10, 2);
} catch (std::overflow_error e) {
std::cout << e.what() << " -> ";
}
std::cout << i << std::endl;
try {
i = intDivEx (10, 0);
} catch (std::overflow_error e) {
std::cout << e.what() << " -> ";
}
std::cout << i << std::endl;
return 0;
}
Esto produce:
5
Divide by zero exception -> 5
y puede ver que arroja y atrapa la excepción de la división por cero.
El %
equivalente es casi exactamente el mismo:
// Integer remainder, catching divide by zero.
inline int intModEx (int numerator, int denominator) {
if (denominator == 0)
throw std::overflow_error("Divide by zero exception");
return numerator % denominator;
}
Debería verificar si i = 0
y luego no.
(Opcionalmente, después de verificarlo, puede lanzar una excepción y manejarla más adelante).
Más información en: http://www.cprogramming.com/tutorial/exceptions.html
Por lo que sé, las especificaciones de C ++ no mencionan nada acerca de dividir por cero excepción. Creo que debes hacerlo tú mismo ...
Stroustrup dice, en "El diseño y la evolución de C ++" (Addison Wesley, 1994), "se supone que los eventos de bajo nivel, como los desbordamientos aritméticos y la división por cero, son manejados por un mecanismo dedicado de nivel inferior en lugar de por excepciones Esto permite que C ++ haga coincidir el comportamiento de otros lenguajes cuando se trata de aritmética, y también evita los problemas que ocurren en las arquitecturas de gran cantidad de pipas donde eventos como dividir por cero son asincrónicos ".
Puedes hacer assert(2 * i != i)
que arrojará una afirmación. Puedes escribir tu propia clase de excepción si necesitas algo más elegante.
do i need to throw an exception or does the computer automatically throws one at runtime?
O necesita throw
la excepción y catch
. p.ej
try {
//...
throw int();
}
catch(int i) { }
O catch
la excepción que arroja tu código.
try {
int *p = new int();
}
catch (std::bad_alloc e) {
cerr << e.what();
}
En su caso, no estoy seguro de si existe alguna excepción estándar para dividir por cero. Si no existe tal excepción, entonces puede usar,
catch(...) { // catch ''any'' exception
}
Actualizado con comentarios de ExcessPhase
GCC (al menos la versión 4.8) le permitirá emular este comportamiento:
#include <signal.h>
#include <memory>
#include <iostream>
int main() {
std::shared_ptr<void(int)> handler(
signal(SIGFPE, [](int signum) {throw std::logic_error("FPE"); }),
[](__sighandler_t f) { signal(SIGFPE, f); });
int i = 0;
std::cin >> i; // what if someone enters zero?
try {
i = 5/i;
}
catch (std::logic_error e) {
std::cerr << e.what();
}
}
Esto configura un nuevo manejador de señal que arroja una excepción y un shared_ptr
al antiguo manejador de señal, con una función de "eliminación" personalizada que restaura el controlador anterior cuando se sale del alcance.
Necesitas compilar con al menos estas opciones:
g++ -c Foo.cc -o Foo.o -fnon-call-exceptions -std=c++11
Visual C ++ también te permitirá hacer algo similar:
#include <eh.h>
#include <memory>
int main() {
std::shared_ptr<void(unsigned, EXCEPTION_POINTERS*)> handler(
_set_se_translator([](unsigned u, EXCEPTION_POINTERS* p) {
switch(u) {
case FLT_DIVIDE_BY_ZERO:
throw std::logic_error("Divide by zero");
...
default:
throw std::logic_error("SEH exception");
}
}),
[](_se_translator_function f) { _set_se_translator(f); });
int i = 0;
try {
i = 5 / i;
} catch(std::logic_error e) {
std::cerr << e.what();
}
}
Y, por supuesto, puede omitir todo el C ++ 11-ishness de esto y ponerlos en una estructura de gestión de RAII tradicional.