1.与异常有关的代码可以分为异常检测部分和异常处理部分。异常检测部分抛出异常,异常处理部分捕获异常并处理异常;
2.当异常被抛出后,立即沿着函数调用链寻找能够处理该异常的代码(这个过程成为堆栈展开)。一层一层向外寻找,直到主函数。如果找到主函数都没有找到处理该异常的代码,程序将调用标准库中的terminate函数终止程序。
3.某个函数内部抛出了异常,而且没有找到处理该异常的代码,则函数立即退出,异常抛出前生成的局部对象将被析构。
从这个角度看,throw有点像return的作用。因而函数有可能不像预期的那样顺序执行,可能在中间某个位置,调用了其他函数,产生了异常后就退出了。非正常退出就可额造成某些对象没有正确析构,造成内存泄漏。也正因为如此,才需要智能指针来保证局部对象都能正确析构。
4.多线程中的异常。主线程是没法捕获其他线程的异常。因此,在多线程程序,某个线程产生了异常,且当前线程中都没有相应的处理异常的代码,则程序调用terminate函数退出。
因为某个线程产生了异常导致程序退出,是不划算的。所以尽可能确保线程能处理所有的异常。
如果主线程需要捕获其他线程的异常,可以采用全局变量的方式。
std::exception_ptr ptr;
void f0()
{
try {
std::string str;
for (int i = 0; i < 5; i++)
{
std::cout << "f0线程启动" << i << std::endl;
std::this_thread::sleep_for(std::chrono::milliseconds(500));
}
str.at(2); //越界访问
throw std::exception("线程中正常抛出异常"); //抛出异常
}
catch (const std::exception& m) {
std::cout <<"子线程输出异常信息:" << m.what() << std::endl;
ptr = std::current_exception();
}
}
int main()
{
std::thread t1(f0);
t1.join(); //主线程等待子线程结束
try {
if (ptr) {
std::cout << "检测到子线程异常" << std::endl;
std::rethrow_exception(ptr);
}
}
catch (std::exception& e)
{
std::cout <<"主线程输出子线程错误信息:" << e.what() << std::endl;
}
std::cout << "主线程退出!" << std::endl;
return 0;
}