Solicitando dos enteros y dividiendo, utilizando excepciones para manejar errores divididos por cero -- ++ campo con beginner campo con error-handling campo con exception camp codereview Relacionados El problema

Prompting for two integers and dividing, using exceptions to handle divide-by-zero errors


10
vote

problema

Español

Estoy tratando de aprender a un manejo de excepciones en C ++.

Quería leer dos enteros y dividirlos e imprimirlos.

El código debe lanzar una excepción cuando el segundo entero es cero, pídale al usuario que vuelva a ingresar el segundo entero y luego complete la operación de división.

codificé algo como esto:

  0.  0.  1.  0.27639320225002106 0.8506508083520399  0.4472135954999579  -0.7236067977499789 0.5257311121191336  0.4472135954999579 0.  0.  1.  -0.7236067977499789 0.5257311121191336  0.4472135954999579  -0.7236067977499789 -0.5257311121191336 0.4472135954999579 0.  0.  1.  -0.7236067977499789 -0.5257311121191336 0.4472135954999579  0.27639320225002106 -0.8506508083520399 0.4472135954999579 0.  0.  1.  0.27639320225002106 -0.8506508083520399 0.4472135954999579  0.8944271909999159  0.  0.4472135954999579 0.  0.  1.  0.8944271909999159  0.  0.4472135954999579  0.27639320225002106 0.8506508083520399  0.4472135954999579 0.7236067977499789  -0.5257311121191336 -0.4472135954999579 -0.27639320225002106    -0.8506508083520399 -0.4472135954999579 0.  0.  -1. 0.7236067977499789  0.5257311121191336  -0.4472135954999579 0.7236067977499789  -0.5257311121191336 -0.4472135954999579 0.  0.  -1. -0.27639320225002106    0.8506508083520399  -0.4472135954999579 0.7236067977499789  0.5257311121191336  -0.4472135954999579 0.  0.  -1. -0.8944271909999159 0.  -0.4472135954999579 -0.27639320225002106    0.8506508083520399  -0.4472135954999579 0.  0.  -1. -0.27639320225002106    -0.8506508083520399 -0.4472135954999579 -0.8944271909999159 0.  -0.4472135954999579 0.  0.  -1. 0.27639320225002106 0.8506508083520399  0.4472135954999579  -0.27639320225002106    0.8506508083520399  -0.4472135954999579 -0.7236067977499789 0.5257311121191336  0.4472135954999579 -0.7236067977499789 0.5257311121191336  0.4472135954999579  -0.8944271909999159 0.  -0.4472135954999579 -0.7236067977499789 -0.5257311121191336 0.4472135954999579 -0.7236067977499789 -0.5257311121191336 0.4472135954999579  -0.27639320225002106    -0.8506508083520399 -0.4472135954999579 0.27639320225002106 -0.8506508083520399 0.4472135954999579 0.27639320225002106 -0.8506508083520399 0.4472135954999579  0.7236067977499789  -0.5257311121191336 -0.4472135954999579 0.8944271909999159  0.  0.4472135954999579 0.8944271909999159  0.  0.4472135954999579  0.7236067977499789  0.5257311121191336  -0.4472135954999579 0.27639320225002106 0.8506508083520399  0.4472135954999579 0.7236067977499789  -0.5257311121191336 -0.4472135954999579 0.27639320225002106 -0.8506508083520399 0.4472135954999579  -0.27639320225002106    -0.8506508083520399 -0.4472135954999579 0.7236067977499789  0.5257311121191336  -0.4472135954999579 0.8944271909999159  0.  0.4472135954999579  0.7236067977499789  -0.5257311121191336 -0.4472135954999579 -0.27639320225002106    0.8506508083520399  -0.4472135954999579 0.27639320225002106 0.8506508083520399  0.4472135954999579  0.7236067977499789  0.5257311121191336  -0.4472135954999579 -0.8944271909999159 0.  -0.4472135954999579 -0.7236067977499789 0.5257311121191336  0.4472135954999579  -0.27639320225002106    0.8506508083520399  -0.4472135954999579 -0.27639320225002106    -0.8506508083520399 -0.4472135954999579 -0.7236067977499789 -0.5257311121191336 0.4472135954999579  -0.8944271909999159 0.  -0.4472135954999579 1  

Sin embargo, se le aconsejó que no usara goto a toda costa.

¿Puede alguien darme alguna forma alternativa?

Original en ingles

I'm trying to learn exception handling in C++.

I wanted to read two integers and divide them and print them.

The code should throw an exception when the second integer is zero, ask the user to re-enter the second integer, and then complete the divide operation.

I coded something like this:

#include <iostream> #include <stdexcept>  using std::cin; using std::cout; using std::endl;  int main() {     int i1, i2;     cout << "Give two integers" << endl;     cin >> i1;     try {         first:         cin >> i2;         if (i2 == 0)             throw 0;         else             cout << i1 / i2 << endl;     }     catch (...) {         cout << "Second integer cannot be zero , please enter a valid integer" << endl;         goto first;     }     system("pause");     return 0; } 

However, I was advised not to use goto at all costs.

Can anyone give me some alternative way?

           
         
         

Lista de respuestas

26
 
vote

Las excepciones son para Casos excepcionales

Estoy tratando de aprender a un manejo de excepciones en C ++.

Este es un muy mal ejemplo para aprender excepciones. Están destinados a situaciones en las que no puedes saber que hay algo mal. Por ejemplo, si desea utilizar std::vector<int> , pero está fuera de la memoria:

  std::vector<int> vec(4000000000LL); // will likely throw std::bad_alloc   

Añadiré un ejercicio para usted al final de esta revisión. Pero echemos un vistazo a su código.

Una revisión de su código

No está utilizando using namespace std , que es una gran ventaja.

Sin embargo, usted incluye <stdexcept> , que no es necesario aquí. No usa ninguna de las excepciones estándar, en su lugar, lanza 0 . Sin embargo, system está en <cstdlib> . Debe incluir eso en su lugar.

A continuación, nombrar.

  int i1, i2;   

Esos nombres no tienen ningún significado. ¿Qué es i1 ? ¿Qué es i2 ? El nombramiento es difícil, pero es importante. Los usará como numerator y denominador , o como dividendo y divisor , así que llámalos apropiadamente :

  std::vector<int> vec(4000000000LL); // will likely throw std::bad_alloc 0  

Deshacerse de std::vector<int> vec(4000000000LL); // will likely throw std::bad_alloc 1

Ahora a su bloque std::vector<int> vec(4000000000LL); // will likely throw std::bad_alloc 2 . No lo hagas Usar. std::vector<int> vec(4000000000LL); // will likely throw std::bad_alloc 3 . Desea repetir el bloque hasta que no haya obtenido std::vector<int> vec(4000000000LL); // will likely throw std::bad_alloc 4 . Podemos hacerlo con un simple std::vector<int> vec(4000000000LL); // will likely throw std::bad_alloc 5 :

  std::vector<int> vec(4000000000LL); // will likely throw std::bad_alloc 6  

una versión "limpia" sin excepciones

Sin embargo, no es así como escribiría el programa, ya que las excepciones están destinadas a casos excepcionales. Yo escribiría

  std::vector<int> vec(4000000000LL); // will likely throw std::bad_alloc 7  

Como puede ver, las excepciones no son realmente necesarias para evitar que el usuario ingrese a un cero. Además, el flujo de control es mucho más fácil de leer.

NOTA QUE std::vector<int> vec(4000000000LL); // will likely throw std::bad_alloc 8 no funciona en Linux, ya que no hay ninguna aplicación llamada std::vector<int> vec(4000000000LL); // will likely throw std::bad_alloc 9 . Si es posible, intente usar sus funciones de IDES para mantener a la ventana de su consola, incluso después de que termine su programa, o haga que la salida haya registrado en algún lugar, o utilícelo en el CMD / Powershell.

Ejercicio de excepciones

Necesitamos un mejor ejercicio para sus excepciones. ¿Qué hay de esto?

  using namespace std0  

Este es un lugar válido para una excepción. Una función tiene solo un solo valor de retorno, por lo que solo puede decirle que algo está mal con una excepción *.

Ejercicio: Trate de usar esa función en su código y manejar la excepción. También muestre la excepción al usuario, probablemente están interesados ​​en lo que salió mal.

Debe usar el código similar a

  using namespace std1  

¿Es decir, no revisa el using namespace std2 ? Tenga en cuenta que esto sigue siendo un ejemplo ideal.

* técnicamente, eso no es cierto. Puede usar referencias, punteros o envolver el valor de retorno en algunos using namespace std3 / variante, pero mantengamos las cosas simples

 

Exceptions are for exceptional cases

I'm trying to learn exception handling in C++.

This is a very bad example to learn exceptions. They are meant for situations where you cannot know that there is something wrong. For example if you want to use std::vector<int> but you're out of memory:

std::vector<int> vec(4000000000LL); // will likely throw std::bad_alloc 

I'll add an exercise for you at the end of this review. But let's have a look at your code.

A review of your code

You're not using using namespace std, which is a great plus.

However, you include <stdexcept>, which isn't necessary here. You don't use any of the standard exception, instead, you throw 0. However, system is in <cstdlib>. You should include that one instead.

Next, naming.

int i1, i2; 

Those names don't have any meaning. What is i1? What is i2? Naming is hard, but it's important. You're going to use them as numerator and denominator, or as dividend and divisor, so call them appropriately:

int numerator, denominator; 

Getting rid of goto

Now to your try block. Don't. Use. goto. You want to repeat the block until you didn't get 0. We can do that with a simple while:

int main (){     int numerator, denominator;      cout << "Give two integers" << endl;     cin >> numerator;       // continue forever                               (1)     while(true) {         try {             cin >> denominator;             if (denominator == 0)                 throw 0; // this will "go to" catch   (2)              cout << numerator / denominator << endl;             break; // we didn't throw, we can stop    (3)         }         catch (...) {             cout << "Second integer cannot be zero , please enter a valid integer" << endl;         }     }      system("pause");     return 0; } 

A "clean" version without exceptions

However, that's not how I would write the program, since exceptions are meant for exceptional cases. I would write

#include <iostream> #include <cstdlib>  int main() {     int numerator, denominator;      std::cout << "Please enter two integers." << std::endl;     std::cin >> numerator >> denominator;      while(denominator == 0) {         std::cout << "Second integer cannot be zero. Try again." << std::endl;         std::cin >> denominator;     }      std::cout << (numerator / denominator) << std::endl;      system("pause");     return 0; } 

As you can see, exceptions aren't really necessary to keep the user from entering a zero. Furthermore, the control flow is a lot easier to read.

Note that system("pause"); won't work on Linux, since there is no application called pause. If possible, try to use your IDEs features to keep your console window alive even after your program finished, or have the output logged somewhere, or use it in the CMD/PowerShell.

Exception exercise

We need a better exercise for your exceptions. How about this?

int divide(int numerator, int denominator) {     if(denominator == 0)         throw "divide: division by zero";     return numerator / denominator; } 

This is a valid place for an exception. A function has only a single return value, so it can only tell you that something is wrong with an exception*.

Exercise: Try to use that function in your code and handle the exception. Also show the exception to the user, they're probably interested in what went wrong.

You should use the code similar to

std::cout << divide(numerator, denominator) << std::endl; 

that is you don't check the denominator. Note that this is still a contrived example.

* Technically, that's not true. You can use references, pointers, or wrap the return value in some struct/variant, but let's keep things simple

 
 
       
       
7
 
vote

Muchas prácticas malas exhibidas aquí ...

En primer lugar, probablemente no deberías estar usando el manejo de excepciones aquí, ya que incompinable ya se señaló en un comentario. Las excepciones deben ser para situaciones verdaderamente excepcionales. Si espera una entrada cero como posibilidad y lo manejará explícitamente, entonces argumentaría que no es excepcional en absoluto. Por lo tanto, se basará un simple 9988776655544330 la prueba de bucle: simplemente mantente en bucle hasta que el usuario ingrese en una entrada válida.

Segundo, sus nombres de variables dejan mucho que desear. ¿Qué son i1 y i2 ? Está bien usar los nombres de la variable de una letra única, como i cuando su propósito es obvio, en cuanto a un índice de bucle, pero ese no es el caso aquí. Por lo tanto, debe usar los nombres de variables descriptivos. Dado que estamos haciendo división, tal vez dividend y divisor serían buenas opciones?

Tercero, system("pause")6 es solo terrible y nunca debe usarse en código real. Supongo que está usando esto para solucionar el hecho de que su entorno de desarrollo cierra el entorno de la consola tan pronto como el programa finaliza la ejecución, en lugar de dejar la ventana en la pantalla para que pueda ver su salida. No use el código como un enganche para que trabaje alrededor de la mala conducta de su IDE, aprenda a configurar su IDE para que haga lo que desea. Si es absolutamente debe confiar en dicho enganche, use cin.get() . Pero realmente no hay una manera "buena" de hacer esto, así que prefiero no hacerlo.

cuarto, si va a tirar excepciones:

  • Nunca arroje enteros, o literales de cuerdas, o cualquier otra cosa que no sea un objeto de excepción como una excepción. Debe lanzar una de las excepciones estándar proporcionadas por la biblioteca estándar (, por ejemplo, , 9988776655544338 ), o debe derivar su propia clase de excepciones de una de estas excepciones estándar y lanzar Una instancia de esa clase. En este caso, estaría bien usando std::domain_error .

  • No use i10 a menos que realmente desee atraparlo> todas las excepciones posibles , que casi nunca quiere hacer. Usted es efectivamente forzado para hacer esto porque está lanzando un entero, pero ya que ya no va a hacerlo, puede captar adecuadamente una instancia de una clase de excepción por Const Referencia: < Código> i11 .

  • No use i12 como muleta para el control de flujo adecuado. Parte de la razón por la que está teniendo problemas para escribir código razonable aquí es porque estabas tratando de cram toda la lógica en una sola función. Usted tuvo un código para recuperar la entrada del usuario, código para llevar a cabo la lógica del programa (la operación de la división) y el código para imprimir el resultado en la misma función ! Romperlo en piezas lógicas. A menudo puede salirse con esto en un programa de juguete, pero es un mal hábito entrar.

Aquí está el código basado en excepciones:

  i13  

y aquí está el código más simple que no usa excepciones. Observe que es exactamente la misma lógica que antes, detectando explícitamente el error y manejándolo, pero no usa excepciones para el control de flujo, solo estructuras de control de flujo normales como bucles (¡que estamos usando de todos modos!):

  i14  

Mientras que lo que ha hecho para evitar i15 es bueno, prefiero siempre 99887776655443316 -quesify todas las referencias. No es que implique una cantidad excesiva de escritura.

 

Lots of bad practices exhibited herexe2x80xa6

First of all, you probably shouldn't be using exception handling here at all, as Incomputable already pointed out in a comment. Exceptions should be for truly exceptional situations. If you expect a zero input as a possibility and are going to explicitly handle it, then I would argue that it is not exceptional at all. Thus, a simple while loop and conditional test would suffice: just keep looping until the user enters a valid input.

Second, your variable names leave a lot to be desired. What are i1 and i2? It's okay to use terse, single-letter variable names like i when their purpose is obvious, as for a loop index, but that's not the case here. Therefore, you should use descriptive variable names. Since we're doing division, maybe dividend and divisor would be good choices?

Third, system("pause") is just terrible and should never be used in real code. I guess you're using this to work around the fact that your development environment closes the console environment as soon as the program finishes execution, instead of leaving the window on the screen so you can see its output. Don't use code as a hitch to work around your IDE's bad behaviorxe2x80x94learn to configure your IDE so it does what you want. If you absolutely must rely on such a hitch, use cin.get(). But there's really no "good" way to do this, so prefer not doing it.

Fourth, if you're going to throw exceptions:

  • Never throw integers, or string literals, or anything else other than an exception object as an exception. You should either throw one of the standard exceptions provided by the standard library (e.g., std::runtime_error), or you should derive your own exception class from one of these standard exceptions and throw an instance of that class. In this case, you'd be fine using std::domain_error.

  • Don't use catch (...) unless you actually want to catch all possible exceptions, which you hardly ever want to do. You are effectively forced into doing this because you're throwing an integer, but since you're not going to be doing that anymore, you can properly catch an instance of an exception class by const reference: catch (const std::domain_error& ex).

  • Don't use goto as a crutch for proper flow control. Part of the reason you were having trouble writing sensible code here is because you were trying to cram all of your logic into a single function. You had code to retrieve input from the user, code to carry out the program logic (the division operation), and code to print the result all in the same function! Break it up into logical pieces. You can often get away with this in a toy program, but it is a bad habit to get into.

Here is the exception-based code:

int Divide(int dividend, int divisor) {     if (divisor == 0)     {         throw std::domain_error("Attempted to divide by zero.");     }     return (dividend / divisor); }  int main() {     int dividend;     int divisor;     std::cout << "Input two integers: " << std::endl;     std::cin >> dividend;     while (true)     {         std::cin >> divisor;         try         {             std::cout << Divide(dividend, divisor) << std::endl;             break;  // division succeeded, so stop looping         }         catch (std::domain_error& ex)         {             std::cout << "The second integer cannot be zero; please enter a valid integer: " << std::endl;         }     }     return 0;   // you can omit this for the main function } 

And here is the simpler code that doesn't use exceptions. Notice that it is exactly the same logic as before, explicitly detecting the error and handling it, but it doesn't use exceptions for flow controlxe2x80x94just normal flow control structures like loops (which we're using anyway!):

int main() {     int dividend;     int divisor;     std::cout << "Input two integers: " << std::endl;     std::cin  >> dividend >> divisor;     while (divisor == 0)     {         std::cout << "The second integer cannot be zero; please enter a valid integer: " << std::endl;         std::cin  >> divisor;     }     std::cout << (dividend / divisor) << std::endl;     return 0;   // you can omit this for the main function } 

While what you've done to avoid using namespace std; is good, I prefer to always explicitly std::-qualify all references. It's not like it entails an excessive amount of typing.

 
 
       
       
3
 
vote

Como una adición a los puntos hechos en otras respuestas, tenga en cuenta que su programa no compila , ya que en C ++ no puede saltar a un bloque i17 ...

  i18  

... y, de hecho, usted también no debería poder saltar a un bloque i19 .

 

As an addition to the points made in other answers, note that your program doesn't compile, since in C++ you cannot jump into a try block...

g++ -std=c++14 -O2 -Wall -pedantic -pthread main.cpp && ./a.out main.cpp: In function 'int main()': main.cpp:13:9: error: jump to label 'first'          first:          ^~~~~ main.cpp:22:14: note:   from here          goto first;               ^~~~~ main.cpp:22:14: note:   enters try block 

... and, indeed, you also shouldn't be able to jump into a try block.

 
 
         
         

Relacionados problema

6  Clase de excepción de C ++ autocontrolada  ( Self implemented c exception class ) 
Escribí mi propia clase de excepciones, derivando de std::runtime_error para tener identificaciones de errores, marcas de tiempo y excepciones internas. Par...

5  Lanzar una excepción que contiene un conjunto anidado de excepciones anteriores  ( Throw an exception which contains a nested set of previous exceptions ) 
Tengo un conjunto de objetos de dominio que intentan encontrar una solución a un problema. El objeto de nivel superior tiene un algoritmo que divide el prob...

6  Manejo de excepciones COM / Códigos ocupados  ( Handling com exceptions busy codes ) 
Este código escribe para sobresalir utilizando la interfaz COM. El tema general es que cualquier manejo de excepciones tiene que manejar la excepción de "Exce...

15  Usando finalmente con declaración de retorno o no  ( Using finally with return statement or not ) 
Me preguntaba si es la forma correcta de colocar siempre la declaración de devolución de una función con una cláusula 9988776665544330 en la cláusula 99887...

9  Proporcionando interfaces de "envoltura" sin marcar para una API con excepciones verificadas  ( Providing unchecked exception wrapper interfaces for an api with checked excep ) 
Bloqueado . Esta pregunta y sus respuestas son bloqueadas porque la pregunta es off-topic pero tiene importancia histórica. Actualmente no está a...

1  Cómo manejar el valor devuelto si se produce una excepción en un código de la biblioteca  ( How to handle returned value if an exception happens in a library code ) 
Hay un código LIB, tratando de analizar un objeto de árbol de elementos. Si ocurre una excepción, devuelve un dict vacío de DICT o un objeto parcialmente cons...

6  Uso de la excepción al registro de la pila de ejecución  ( Use of exception to log execution stack trace ) 
He estado de ida y vuelta con un colega sobre el uso de Throwable.fillInStackTrace . Este Log clase está destinado a envolver el Fachada de registro simpl...

14  ¿Cuál es tu opinión sobre un método de tiro ()?  ( What s your opinion on a throw method ) 
Bloqueado . Esta pregunta y sus respuestas son bloqueadas porque la pregunta es off-topic pero tiene importancia histórica. Actualmente no está a...

7  ¿Es esta la forma incorrecta de manejar agregateException con tareas?  ( Is this the wrong way to handle aggregateexception with tasks ) 
Estoy viendo un gran código como este en mi nuevo sitio try { Task.Factory.StartNew(() => { ... ...

1  Mensaje de excepción de java  ( Java exception message ) 
Tengo algunas clases de excepción personalizadas que creé simplemente por tener mi propio mensaje de excepción: public class DivideByZeroException extends ...




© 2022 respuesta.top Reservados todos los derechos. Centro de preguntas y respuestas reservados todos los derechos