在C ++中纠正异常

时间:2010-04-27 04:40:26

标签: c++ exception-handling

我正在学习如何处理我的C ++代码中的错误。我写了这个例子,它寻找一个名为some file的文本文件,如果找不到它会引发异常。

#include <iostream>
#include <fstream>
using namespace std;

int main()
{
int array[90];
try
{
   ifstream file;
   file.open("somefile.txt");
   if(!file.good())
    throw 56;
}
catch(int e)
{
    cout<<"Error number "<<e<<endl;
}
return 0;
}

现在我有两个问题。首先,我想知道我是否正确使用了例外。第二,(假设第一个是真的)使用它们对If else语句有什么好处?

6 个答案:

答案 0 :(得分:23)

“正确”是一种价值判断,但(与其他类不同),异常类是单一层次结构的主要好处,所以我通常会建议抛出从std::exception派生的东西,不是< / em>只是一个int。

其次,可以肯定的是,不正确的文件名是否足够意外,无法作为抛出异常的理由。

关于利益与if / else声明:有一对。首先,异常允许您隔离处理错误的代码,因此代码的主要思想和可读性不会在错误处理的迷宫中丢失。其次,当你在抛出和捕获异常之间有几层代码时,抛出异常的代码可能不知道应该如何处理它。例如,您的代码使用std::cout来报告问题 - 但大多数此类代码会报告std::cerr上的错误。您可以从一个更改为另一个,而无需对尝试打开文件的代码进行任何更改(可能在库中很深,并且不知道哪个应该用于此应用程序 - 并且可能在应用程序中使用两者都错了,MessageBox是首选。)

答案 1 :(得分:8)

首先,我想知道我是否正确使用例外。
是的,但通常您希望您的异常派生自std :: exception。

其次,(假设第一个是真的)使用它们对If else语句有什么好处?
对于给定的例子,没有。有例外的好处来自你 许多深层嵌套函数,像这样。

#include <stdexcept>
#include <iostream>
#include <string>

void anErrorFunc(const std::string& x)
{
    ifstream file;
    file.open(x);
    if (!file)
        throw std::runtime_error("Could not open file");
}

void someOtherFunction(const std::string& y)
{
    //Do stuff
    anErrorFunc(y);
    //Do other stuff
}

int main()
{
    try {
        someOtherFunction("somefile.txt");
    } catch (std::exception &ex) {
        std::cout << "Ouch! That hurts, because: "
            << ex.what() << "!\n";
    }
}

请注意,异常将在main()someOtherFunction中捕获 不必担心处理通过故障返回代码。

答案 2 :(得分:3)

嗯,您正确使用异常,因为您的代码没有任何问题。也就是说,通常我们不会抛出原始类型(即使你可以)。抛出一个派生自std::exception的对象通常是一个更好的主意,甚至更好地抛出一个同样是boost::exception的std ::异常。

当事情非常简单并且处理代码和抛出代码在同一个函数中时,那么实际上没有理由使用异常而不是if语句(事实上,如果使用它会更快更有效......在那个特殊情况下)。但是,在大多数情况下,发现错误并且需要报告错误的点远离要处理错误的逻辑。在许多情况下,错误恢复逻辑特定于所讨论的应用程序,并且发现错误的逻辑无法做出关于如何从错误中恢复的明智选择,因此需要抛出。

异常处理的另一个好处是异常的类型可用于传达发生的错误类型。通常,异常层次结构中的类型比最终在C代码中使用的那些错误代码更有意义。此外,您不能轻易忽略异常,因为您可以忽略错误代码;虽然你可以忽略一个例外,它会导致程序死于可怕的死亡。相反,如果C函数返回错误状态代码,并且您忽略它,则可以继续执行并获得静默错误的结果......从这个意义上说,使用异常比使用错误代码更安全。

您可能也有兴趣阅读exceptions and error handling from the C++ FAQ Lite

答案 3 :(得分:2)

您没有正确使用例外。您的代码具有更简单的等价物,没有例外,它提供相同的行为。

例外情况是指无法使用其他方法测试函数调用的结果。在这种情况下,你可以,所以你不应该使用例外。

作为必然结果,你不应该在同一个函数体中抛出并捕获异常 - 只需做你想做的任何事情而不是抛出它。

答案 4 :(得分:1)

从语法上讲,你的代码是正确的。习惯上说,也许不是那么多 - 或者至少这取决于背景。如果文件无法打开,那么我们可以在if( !file.good() )内部进行处理,如果它完全通用且可能发生的话。例如,如果用户要求在文本编辑器中打开文件,那么该文件不存在就完全合理且常见。另一方面,如果编辑器找不到拼写语料库文件,那么这意味着(可以说)非常错误。该程序可能没有安装,或者用户弄乱了该文件 - 一切皆有可能。

在C ++中,我们使用例外情况的例外。也就是说,这些案件实际上并非意味着发生,而且我们并不“接受”这种情况。这与用户文件未打开,无效用户输入或无互联网连接相反:这些都是完全有效的事情发生的例子,常见情况,我们期望在程序运行中迟早发生的事情。它们不是例外

现在,与条件相比,使用异常有什么好处?请允许我将此问题扩展到任何其他跳转(goto)机制:返回以及条件。如果您想说的话,例外情况会更具表现力:如果您正在处理特殊情况,则使用例外。异常也比普通条件更多地完成,其方式类似于虚函数比条件更多的完成。正确的代码块将根据异常执行,但正确的范围将根据处理程序处理异常。

与条件相比,异常还有其他优点:异常将错误处理与其他代码分开。它们允许将任意数据和操作与错误状态相关联。它们允许通信成功状态(通过return)以及错误状态(通过throw)。列表继续......

从技术上讲,在最低级别,例外是复杂的跳转机制。回到the butterfly-days人们发明了if条件作为一个有点复杂的goto,以增强表达能力(因为goto可以用于任何事情)并减少程序员错误。诸如C for循环之类的循环结构本质上也是具有闪光和彩虹色的复杂跳跃,同样用于减少错误和增强表现力。出于同样的原因,C ++引入了新的铸造运算符。

所以:异常不是魔术,只是与条件和循环相比,场景中有些新东西。当你不打算使用它们时,不要使用它们,就像你真正想要使用条件时不使用循环一样。

答案 5 :(得分:1)

从语法上讲,你所做的是对的。风格方面,正如其他人所指出的那样,你应该抛出一些来自std :: exception的东西。

关于你的问题的第二部分,我想详细介绍一下。

例外的全部内容是将政策与实施分开。正如Billy ONeal所说,在if语句不会更好的同一函数中使用异常,你完全没有任何好处。你需要在函数调用中深深嵌套它才有意义。

在大多数代码中,您的高级代码具有足够的信息和上下文来知道如何处理错误,但没有检测它们的机制。您的低级代码可以检测错误,但没有处理它们所需的信息。

应对此问题的传统方法 - 返回错误代码 - 存在一些问题:

  1. 它将带有错误处理代码的代码混乱到实际逻辑被混淆的程度。
  2. 它依赖程序员不懒惰并检查每个错误代码返回,这是一个经常是愚蠢的假设。 (C程序员,老实说:你最后一次检查printf的返回值是什么时候?)
  3. 它将错误检查和处理的开销添加到每个函数调用是否有错误。
  4. 例外解决了这些问题(取得了不同程度的成功)。

    • 异常通过在检测点和处理点仅具有与异常相关的代码来解决#1。干预功能不会因处理他们自己不感兴趣的晦涩错误而混淆,也不会处理。
    • 他们通过强制处理来解决#2。你不能忽视一个例外。你必须对它们采取行动。 (懒惰的程序员仍然可以捕获所有异常,然后忽略它们,但是现在突出显示他们对程序的无能为力了。)
    • 他们在不使用时解决了#3(当没有天真地实施时)成本接近于零,虽然实际使用时成本非常非常高。

    这并不是说异常是错误处理的最终全部/全部。缺点:

    1. 使用时,例外情况通常非常昂贵。如果表现至关重要,他们必须避开,尽管他们有优势。
    2. 异常导致代码非常不透明。它们是非本地控制转移 - 实际上是goto语句的更安全版本,但跨功能。一个例外可以在源代码文件中从代码深处的数百个层转移控制,甚至与您正在处理的文件略有关系(实际上,甚至很可能无法访问)。这种“远距离的怪异行为”可能使代码很难弄明白。
    3. “检查异常”对于噪声生成实际上可能比旧式if处理更糟糕。你知道,它们比ifswitch语句更加冗长,并且你必须处理代码甚至编译的已检查异常的事实使它们成为很多情况。
    4. 由于它们的使用成本通常很高,因此不小心使用它们进行所有错误处理会使代码变得缓慢而臃肿。