我正在尝试理解C ++中的错误处理。
我已经读过使用try,throw,catch比使用带有返回值的if语句更好的样式和更简单。但我不确定我是否真的理解如何尝试,抛出,抓住工作。我在下面做了一个简单的例子,很高兴获得有关任何问题或不良风格的反馈。我的目标是从检查另一个计算结果的示例中创建一个函数。
以下是关于try,throw,catch的问题: (1)捕获声明是否应包含在我的功能中?或者它应该在其他地方,例如在main()或初始计算的函数中?
(2)使用try,catch,throw这个简单的东西(我希望改进我的风格)是否过度了?
(3)如果有错误,我想终止程序。我该怎么办?或者“捕获”是否意味着这是自动完成的?
(4)我不明白使用cerr。为什么不使用cout?我在这里正确使用过cerr吗?我是否也应该在if / else语句中使用它?
非常感谢您的帮助。
以下是我做的例子:
double calculated = 10.2; // from previous calculation
double tolerance = 0.3; // I can set this in this function
double valueWanted = 10.0; // from previous calculation
const int calcError = 5; // I picked this number randomly to use for indicating an error
try
{
if (fabs(fTargetValue - fCalculated) <= fTolerance)
cout << "Result is within range.";
else
cout << "Failed.";
throw calcError;
}
catch (const int calcError)
{
cerr << "The calculation failed.\n" << endl;
}
答案 0 :(得分:4)
那是很多问题。我会尝试给你一些提示:
(1)不要在函数中包含try-catch。抛出异常是为了告诉外面世界发生了什么事。如果你可以处理你的函数中的问题,不要扔掉^^一个好的错误处理通常是在ASAP(在调用者中)或远在main中的通用处理程序中捕获错误,以处理正常未处理的错误
(2)根据经验,对......特殊事物使用例外。错误是特殊事物的良好候选者。在数学库中可能会出现溢出或除零等异常。你必须决定,但总的来说处理异常错误是好的。
(3)抓住并不意味着你的计划将结束。事实上恰恰相反。通过捕获异常,你说你将处理问题^^如果你想终止,简单程序中的一个简单方法是不捕获异常,因为未捕获异常的默认行为是程序终止^^相反,你可以在某个catch块中显式终止你的程序。
(4)cerr就像cout一样,但它是一个不同的文件描述符。这意味着外部程序可以区分cerr和cout。它用于错误,但这对外部程序来说并不重要。
MY2C
答案 1 :(得分:2)
好的,首先你的例子会抛出,因为你在else
之后没有范围括号。因此,无论结果是否在范围内,每次都只执行cout << "Failed.";
并执行throw calcError
。将其更改为:
else
{
cout << "Failed.";
throw calcError;
}
如果抛出异常,代码将在您定义的catch
块内开始,说明计算失败。
如果结果在范围内(永远不会调用throw
),代码将在 catch块后直接开始执行。
当你抛出一个类型时,那个类型会到达catch处理程序。这允许您为不同类型定义catch处理程序。在这种情况下,您正在抛出(和捕获)const int
。这一切都很好。通常,我们抛出std::exception
或其衍生物。您自己的异常类可以包含与错误相关的信息。在您的情况下,您可以包含一个超出范围的简单消息,或者确实包含失败的const int。
答案 2 :(得分:2)
catch语句应该在抛出的第一个函数中(可能在抛出的函数中),该函数可以从异常中恢复并允许程序正常继续。
是的,如果您希望抓住它,那就没有任何意义。此外,您的正常程序流程不应抛出。根据经验,只有当你遇到一种你并不真正期望的情况时才会抛出。例外被称为例外,因为它们发生在特殊情况下。通常,在与程序环境交互时,使用异常的好时机。您通常希望某些事情能够发挥作用,例如能够分配内存,打开文件,从网络设备接收完整的数据包等。所有这些情况都会导致抛出异常。此外,如果您的程序收到输入,它应该最初验证它。但是,稍后,在处理过程中,如果应该已经被验证拒绝的数据有问题,例如由于奇怪的输入数据而导致零除,这也是一种特殊情况。如果您在预期的事情发生时过多地依赖异常,那么程序的流程和逻辑就会变得过于难以推理,并且程序维护也会变得不必要。
如果出现错误,请不要抓住。如果没有catch,异常将一直到你的main函数,然后从那里转到运行时将终止你的程序。并且,对于某些O.S.s,例如windows,这将导致创建一个minidump文件,您可以使用它来调试程序,以找出导致它终止的异常。
cerr和cout只为您提供了两种从程序输出信息的方法。如果另一个程序使用程序的输出来执行某些操作,它将读取cout并期望理解它。这意味着如果你想写出消费程序无法理解的错误或警告,你必须将它们写入cerr,这样你就不会混淆正在阅读程序正常cout输出的第二个程序。
答案 3 :(得分:2)
C ++标准有一些可以从中派生的异常类。我建议你这样做而不是投掷和捕捉POD。它也不难,并且会改进(并指定错误的类型),如此
class CalculationError : std::invalid_argument
{
public:
CalculationError(std::string const& msg)
: std::invalid_argument(msg)
{}
};
要快速了解异常层次结构,请转到http://www.richelbilderbeek.nl/CppExceptionHierarchy.htm
问题是:当您抛出POD类型时,没有附加消息。抛出异常的重要部分是能够写出可能出错的消息,以及如何解决它。抛出int时无法做到这一点。
C ++中有三个输出流:log,cerr和cout。它们中的每一个都以不同的方式表示,这意味着,在启动程序时,您可以使用命令行过滤掉每个流。这对于调试非常有用,因为您可以通过cerr进行过滤,看看您的程序是否未通过测试。
示例:my_program > out.txt 2> log.txt
(cout转到out.txt,其他转到log.txt)
但是,我建议不要只是咒骂。通常,捕获点是反转程序状态!例如,如果您尝试分配动态数组,并且失败,那么catch将负责在重新抛出之前再次破坏数组。否则,你会遇到很多内存泄漏等等。
值得注意的是,一旦被抓住,异常就会被“吞噬”。如果你不能或不想在这里处理错误,最好写
catch(/* Error to be caught */)
{
throw; // Rethrows original exception, propagating it upwards
}
如果你想要一些关于此的好文献,Herb Sutter写了一本名为Exceptional C++
的书,它以实用和启发的方式涵盖了异常安全(imo)。如果您想知道何时以及为什么需要抛出异常,那么绝对值得一试。
希望这有帮助!
答案 4 :(得分:1)
难道你没有忘记围绕其他案件的阻拦
try
{
if (fabs(fTargetValue - fCalculated) <= fTolerance)
cout << "Result is within range.";
else {
cout << "Failed.";
throw calcError;
}
}