我正在编写一个程序,该程序使用异常处理找到二次方程的根,我想知道是否有一种方法可以简化程序,我有一堆用于捕获案例的空类
// Program to solve quadratic equation
#include <iostream>
#include <cstdlib>
#include <cmath>
using namespace std;
class all_zero{
};
class negative{
};
class zero_division{
};
void roots(double a, double b, double c) throw (all_zero,negative,zero_division);
int main(){
double a, b, c; // coefficient of ax“2 + bx + c= 0
cout << "Enter the three coefficients \n";
cin >> a >> b >> c;
try
{
roots(a, b, c);
}
catch(all_zero) {cout << "All values are zero \n";}
catch(negative) {cout << "Square root of negative values is not defined \n";}
catch(zero_division) {cout << "Division by zero, not defined \n";}
return 0;
}
void roots(double a, double b, double c) throw (all_zero,negative,zero_division){
double x1, x2; // The two roots
double temp;
if(!(a== 0 && b== 0 && c==0)){
if(a != 0){
temp = b*b - 4*a*c;
if(temp >= 0){
x1 =(-b + sqrt(temp))/2*a;
x2 = (-b - sqrt(temp))/2*a;
cout << "The two roots are: "<< x1 <<" and " << x2 << endl;
}else{throw negative();}
}else{throw zero_division();}
}else{throw all_zero();}
}
有什么方法可以让我没有空类或将它们放入结构体的方法吗?
答案 0 :(得分:2)
请注意,您使用的 dynamic excpetion specification 在 C++11 中已弃用,并在 C++17 中删除:
void roots(double a, double b, double c) throw (all_zero,negative,zero_division)
// ^^
你可以删除它。
using namespace std;
是 considered bad practice。
不要使用 std::endl
添加换行符。 std::endl
添加一个新行并刷新流。大多数时候这是不必要的。使用 '\n'
添加换行符。
有什么方法可以让我没有空类或将它们放入结构体的方法吗?
实际上,我不认为为不同类型的异常设置单独的类有什么坏处。您的方法的缺点是“什么”在 catch
中,而不是异常的一部分或来自异常发生的地方。我将向您展示第一个(消息是类型的一部分),希望您能看到如何实现后者(消息来自抛出异常的地方)。
您可以从 std::runtime_error
继承,它提供了一个 what()
方法,该方法返回传递给构造函数的字符串。这也使其他人更容易捕获您的异常,因为 std::runtime_error
反过来继承自 std::excpetion
,它是所有标准异常的基类:
// Program to solve quadratic equation
#include <iostream>
#include <cstdlib>
#include <cmath>
#include <stdexcept>
struct all_zero : std::runtime_error {
all_zero() : std::runtime_error("All values are zero") {}
};
struct negative : std::runtime_error {
negative() : std::runtime_error("Square root of negative values is not defined") {}
};
struct zero_division : std::runtime_error {
zero_division() : std::runtime_error("Division by zero, not defined") {}
};
void roots(double a, double b, double c);
int main(){
double a, b, c; // coefficient of ax“2 + bx + c= 0
std::cout << "Enter the three coefficients \n";
std::cin >> a >> b >> c;
try
{
roots(a, b, c);
}
catch(std::runtime_error& ex) {std::cout << ex.what() << '\n';}
return 0;
}
void roots(double a, double b, double c) {
double x1, x2; // The two roots
double temp;
if(!(a== 0 && b== 0 && c==0)){
if(a != 0){
temp = b*b - 4*a*c;
if(temp >= 0){
x1 =(-b + sqrt(temp))/2*a;
x2 = (-b - sqrt(temp))/2*a;
std::cout << "The two roots are: "<< x1 <<" and " << x2 << "\n";
}else{throw negative();}
}else{throw zero_division();}
}else{throw all_zero();}
}
注意要捕获异常作为引用,否则派生异常时会出现object slicing。通常,您不知道会捕获什么类型的异常,因此您需要小心避免这种情况发生。
或者,您可以使用更通用的异常类型 (roots_exception
?),而不是将“what”硬编码到 throw
时将其传递给构造函数。
您可能还有更多可以改进的地方,我建议您https://codereview.stackexchange.com/。
PS:我试图将样式放在一边,尽管您应该更改的另一件事是从函数返回结果,而不仅仅是将其打印到屏幕上。如果您想将结果用于其他计算,目前还不能。也许您对如何返回两个值感到困惑。 std::pair
让一切变得简单:
std::pair<double,double> roots(...) {
// ...
return {x1,x2};
}
然后:
try {
auto result = roots(a,b,c);
std::cout << "The two roots are: "<< result.first <<" and " << result.second << "\n";
}
答案 1 :(得分:0)
我的输入:
/2*a
部分应为 /(2*a)
。a== 0 && b== 0 && c==0
)。您已经检查是否 a == 0
,因此如果 a != 0
但 b
和 c
是,解决方案将是 0
,这应该是有效的。else
。尽早抛出,以免嵌套较深的 if
。using namespace std;
considered bad practice?#include <cmath>
#include <cstdlib>
#include <iostream>
#include <stdexcept>
#include <tuple> // std::tuple - used for returning multiple values here
struct solver_error : std::runtime_error { // your solver's error category
using std::runtime_error::runtime_error;
};
struct unknown_error : solver_error {
unknown_error() : solver_error("x is unknown") {}
};
struct linear_error : solver_error {
linear_error() : solver_error("cant solve linear equation") {}
};
struct complex_error : solver_error {
complex_error() : solver_error("cant solve complex equation") {}
};
// add more exceptions to the solver_error exception category if needed
// now returns two values and a bool to indicate if the solution is complex
std::tuple<double, double, bool> solve_for_x(double a, double b, double c) {
if(a == 0) throw linear_error(); // throw early
/* ... or solve it as a linear equation:
if(a == 0) {
if(b == 0) throw unknown_error(); // both a and b == 0, x is unknown
double res = -c / b;
return {res, res, false}; // only one x and not complex
}
*/
double div = 2 * a;
double temp = b * b - 4 * a * c;
if(temp < 0) throw complex_error();
/* ... or solve it as a complex equation
if(temp < 0) {
double sqr = sqrt(-temp); // sqrt of the positive temp instead
return {-b / div, sqr / div, true}; // real, imaginary and complex==true
}
*/
// real quadratic
double sqr = sqrt(temp);
double x1 = (-b + sqr) / div; // corrected formula (div = 2*a)
double x2 = (-b - sqr) / div; // corrected formula
return {x1, x2, false}; // return the two values and complex==false
}
int main() {
// make the user aware of what the coefficients are for
std::cout << "Enter the three coefficients a, b and c in the equation\n"
"ax^2 + bx + c = 0\n";
double a, b, c;
try {
// check that extracting the numbers succeeds
if(not(std::cin >> a >> b >> c)) throw std::runtime_error("input error");
// get the three return values via structured bindings
auto [first, second, complex] = solve_for_x(a, b, c);
std::cout << "x = ";
if(complex) // complex quadratic
std::cout << first << " ± " << second << 'i';
else if(first == second) // linear
std::cout << first;
else // real quadratic
std::cout << '{' << first << ',' << second << '}';
std::cout << '\n';
} catch(const solver_error& ex) {
// catching the generic error category by "const&"
std::cerr << "Solver error: " << ex.what() << '\n';
return 1;
} catch(const std::runtime_error& ex) {
std::cerr << ex.what() << '\n';
return 1;
}
}