捕获异常:除以零

use*_*424 35 c++ exception-handling

当我尝试除以0时,下面的代码没有捕获异常.我是否需要抛出异常,或者计算机是否在运行时自动抛出一个异常?

int i = 0;

cin >> i;  // what if someone enters zero?

try {
    i = 5/i;
}
catch (std::logic_error e) {

    cerr << e.what();
}
Run Code Online (Sandbox Code Playgroud)

pax*_*blo 58

您需要自己检查并抛出异常.整数除以零在标准C++中不是例外.

浮点除以零,但至少具有处理它的具体方法.

ISO标准中列出的例外情况是:

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;
}
Run Code Online (Sandbox Code Playgroud)

你会认为这overflow_error对于表示除以零是理想的.

但是domain_error(5.6虽然我认为这与先前的迭代没有改变)部分明确指出:

如果C++11或的第二个操作数/为零,则行为未定义.

因此,它可能抛出该(或任何其他)异常.它还可以格式化您的硬盘并嘲笑地笑:-)


如果你想实现这样的野兽,你可以使用类似于%以下程序的东西:

#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;
}
Run Code Online (Sandbox Code Playgroud)

这输出:

5
Divide by zero exception -> 5
Run Code Online (Sandbox Code Playgroud)

并且您可以看到它抛出并捕获除零除外的情况.


intDivEx等效几乎是一模一样的:

// 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;
}
Run Code Online (Sandbox Code Playgroud)

  • 由于函数的域是定义函数的有效输入的集合,因此`domain_error`更适合扔在这里吗? (5认同)

Tom*_*Tom 18

更新了ExcessPhase的评论

GCC(至少版本4.8)将允许您模拟此行为:

#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();
    }
}
Run Code Online (Sandbox Code Playgroud)

这将设置一个新的信号处理程序,它抛出一个异常,并设置一个shared_ptr旧的信号处理程序,它带有一个自定义的"删除"功能,可以在旧的处理程序超出范围时恢复它.

您需要至少使用以下选项进行编译:

g++ -c Foo.cc -o Foo.o -fnon-call-exceptions -std=c++11
Run Code Online (Sandbox Code Playgroud)

Visual C++也会让你做类似的事情:

#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:
                case INT_DIVIDE_BY_ZERO:
                    throw std::logic_error("Divide by zero");
                    break;
                ...
                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();
    }
}
Run Code Online (Sandbox Code Playgroud)

当然,您可以跳过所有C++ 11-ishness,并将它们放在传统的RAII管理结构中.

  • 为什么要使用shared_ptr?据我了解,使用 RAII 卸载处理程序。但如果你不共享这个指针,最好使用 unique_ptr,甚至自定义 RAII 结构。 (2认同)
  • @Tom 是的,它是关于捕捉除以零,但我想说的是我认为 FLT_DIVIDE_BY_ZERO 和 SIGFPE 仅用于浮点异常,并且不认为 _integer_ 除以零(本文的主题) post) 可能会被这两个信号中的任何一个捕获。 (2认同)
  • 啊,我明白你的意思,但不,SIGFPE 也用于整数除以零。单一 Unix 规范将 SIGFPE 定义为“错误的算术运算”。FLT_DIVIDE_BY_ZERO 确实是针对浮点的,有对应的 INT_DIVIDE_BY_ZERO;我会更新答案。 (2认同)

May*_*ank 7

据我所知,C++规范没有提及除零除外的任何内容.我相信你需要自己做...

Stroustrup在"C++的设计和演变"(Addison Wesley,1994)中说,"低级事件,例如算术溢出和除以零,假定由专用的低级机制而不是异常处理这使C++在算术运算时能够匹配其他语言的行为.它还避免了在流水线严重的体系结构中出现的问题,其中除以零之类的事件是异步的."