About
RSS

Bit Focus


C++ 异常不怎么常见知识点

Posted at 2011-04-11 13:42:28 | Updated at 2021-09-20 02:50:52

    之前也反复有人推荐 Google 的 C++ Coding Style, 昨天看到一位老师写的解读 Google C++ code style 谈对 C++ 的理解, 文后的讨论大多集中在 Google 不允许 C++ 异常. 我这里就不多插嘴了, 不过有些关于异常的基本情况还是想略聊一聊.
    文中的例子使用 g++ 4.5.2 和 clang++ 2.9 编译测试通过.

    如果一个函数声明了异常声明与该函数实际抛出的异常不匹配, 那么这个异常不会被捕获到. 也就是说 catch (...) 不是银弹.
#include <stdexcept>

void func_throws() throw (std::out_of_range)
{
    throw std::logic_error("danger!");
}

int main()
{
    try {
        func_throws();
    }
    catch (std::out_of_range) {}
    catch (std::logic_error) {}
    catch (...) {}
    return 0;
}
    补丁: std::set_unexpected 函数与不预期的异常

    而一个有趣的 C++ 规定是, 如果一个函数没有任何异常声明, 则该函数有可能抛出任何异常. 这个规定的一个隐喻是, 一切 C 函数都有可能抛出任何异常. 虽然 C 连异常是什么都不知道, 不过没关系. 因为 C 函数是异常透明的.
    比如下面的例子
#include <stdexcept>

extern "C" void exception_pass();

void func_throws()
{
    throw std::logic_error("danger!");
}

void upper_cpp_func()
{
    exception_pass();
}

int main()
{
    try {
        upper_cpp_func();
    } catch (std::logic_error) {
        return 0;
    }
    return 1;
}

extern "C" void exception_pass()
{
    func_throws();
}
    在异常发生后的 stack unwind 阶段, C 函数调用栈帧与 C++ 函数栈帧的处理方式相同. 执行结果是一样的. 但这并不意味着一种好的 C/C++ 编程方式, C 是没有 RAII 的!
    另外, 分离编译再链接时行为会不同, 可能出现异常无法被捕获的情况.
/* a.cpp */
#include <stdexcept>

extern "C" void exception_pass();
extern "C" void upper_c_func();

void func_throws()
{
    throw std::logic_error("danger!");
}

void upper_cpp_func()
{
    upper_c_func();
}

int main()
{
    try {
        upper_cpp_func();
    } catch (std::logic_error) {
        return 0;
    }
    return 1;
}

extern "C" void exception_pass()
{
    func_throws();
}

/* b.c */
void exception_pass();

void upper_c_func()
{
    exception_pass();
}

/* to compile */
g++ a.cpp -c
gcc b.c -c
g++ a.o b.o -o a.out

/* or */
clang++ a.cpp -c
clang b.c -c
clang++ a.o b.o -o a.out
    因为 b.c 在被编译时函数栈没有加入异常信息 (这次是真的 C 了). 所以 C 和 C++ 这样混着乱搅风险相当大.

    Python 或 Java 这样的语言能够在虚拟机一层检测低级错误 (这里的 "低级" 指的是靠近底层, 不是说程序员脑子进水) 并向上报告异常. 如果把操作系统想象成 C++ 的虚拟机, 实际上很多错误也能变成异常, 只是没有 stacktrace 而已.
    甚至一些 CPU 指令级别的错误, 得益于 POSIX signal 机制, 都能高效的被直接包装成 C++ 异常. 请参考 C++ 中捕获整数除零错误. 由于 signal 的发生相当于在当前调用栈上直接垒上另一个栈帧, 所以没问题, 果断抛吧.

Post tags:   Exception Handling  C  C++

Leave a comment:




Creative Commons License Your comment will be licensed under
CC-NC-ND 3.0


. Back to Bit Focus
NijiPress - Copyright (C) Neuron Teckid @ Bit Focus
About this site