1. 异常
异常通常用于处理逻辑上可能发生的错误
在C++98中,提供了一套完善的异常处理机制,直接在程序中将各种类型的异常抛出,从而强制终止程序的运行。
1.1 基本语法
当函数抛出异常时,程序会停止执行,并显示异常信息。异常处理程序可以通过catch
语句捕获并处理异常。如果程序没有捕获异常,程序将终止。
在C++中,栈的解旋是在try
块中的函数调用完毕后自动完成的。这意味着在try
块中的函数调用可能会抛出异常,但不会影响栈的解旋。在catch
块中处理异常时,栈的解旋会被暂停,直到catch
块处理完毕后,栈的解旋才会恢复。
#include <iostream>
using namespace std;
void func() {
int x = 10;
int* p = &x;
*p = 20;
throw *p;
}
void catch_func(int a) {
std::cout << "Caught exception: " << a << std::endl;
}
int main() {
try {
func();
} catch (int e) {
catch_func(e);
}
return 0;
}
heheda@heheda:~/Linux/c++11$ g++ 8.noexcept.cpp -o app
heheda@heheda:~/Linux/c++11$ ./app
Caught exception: 20
【总结】noexcept | 爱编程的大丙 (subingwen.cn)
异常被抛出后,从进入try块起,到异常被抛掷前,这期间在栈上构造的所有对象,都会被自动析构。析构的顺序与构造的顺序相反。这一过程称为栈的解旋。----来自爱编程的大丙
1.2 异常接口声明
🦝① 显示指定可以抛出的异常类型
divide 函数后添加了 throw 异常接口声明,其参数可以表示抛出的异常类型,分别为 int 和 MyException 类型
#include <iostream>
using namespace std;
struct MyException {
MyException(string str):msg(str) {}
string msg;
};
double divide(int a, int b) throw(MyException,int) {
if(b == 0) {
throw MyException("divide by zero");
}
return a / b;
}
int main() {
try{
double val = divide(10, 0);
cout <<"value: "<< val << endl;
}catch(int e) {
cout << "catch except: " << e << endl;
}catch(MyException e) {
cout << "catch except: " << e.msg << endl;
}
return 0;
}
运行结果:
catch except: divide by zero
🦝② 不抛出任何异常
#include <iostream>
using namespace std;
struct MyException {
MyException(string str):msg(str) {}
string msg;
};
double divide(int a, int b) throw() {
if(b == 0) {
throw MyException("divide by zero");
}
return a / b;
}
int main() {
try{
double val = divide(10, 0);
cout <<"value: "<< val << endl;
}catch(int e) {
cout << "catch except: " << e << endl;
}catch(MyException e) {
cout << "catch except: " << e.msg << endl;
}
return 0;
}
在divide 函数后添加了throw异常接口声明,其参数列表为空,表示该函数不允许抛出异常
2. noexcept
double divisionMethod(int a, int b) noexcept(常量表达式);
🦝① 简单地在函数声明后加上 noexcept 关键字
🦝② 可以接受一个常量表达式作为参数,常量表达式的结果会被转换成一个bool类型的值:
- 值为 true,表示函数不会抛出异常
- 值为 false,表示有可能抛出异常这里
- 不带常量表达式的noexcept相当于声明了noexcept(true),即不会抛出异常
noexcept | 爱编程的大丙 (subingwen.cn)
----来自爱编程的大丙
在C++11中弃用了例如上文的动态异常声明 throw(MyException, int)这一特性,表示函数不会抛出异常的动态异常声明 throw() 也被新的 noexcept 异常声明所取代
noexcept
是C++11中引入的一个关键字,用于指定函数抛出异常的情况。当函数声明中使用noexcept
时,编译器可以推断函数不会抛出异常,从而提高代码的执行效率。
🦥noexcept 与 throw动态异常声明的区别:
🦝① 基于异常机制的throw()会带来一些额外的开销,若出现函数抛出异常,会导致函数栈被依次地展开(栈解旋),并自动调用析构函数栈上的所有函数
🦝② noexcept 修饰的函数如果抛出了异常,编译器可以选择直接调用 std::terminate() 函数来终止程序的运行,这比基于异常机制的 throw() 在效率上会高一些
#include <iostream>
using namespace std;
void func() noexcept {
std::cout << "This function does not throw exceptions" << std::endl;
}
int main() {
try {
func();
} catch (...) {
std::cout << "Exception caught" << std::endl;
}
return 0;
}
在这个例子中,func
函数使用noexcept
关键字指定,因此编译器可以推断出函数不会抛出异常,从而提高代码的执行效率。而main
函数中的func
调用不会抛出异常,因此程序可以正常运行。
#include <iostream>
using namespace std;
double divide(int a, int b) noexcept {
if(b == 0) {
cout << "divide by zero!!!" << endl;
return -1;
}
return a / b;
}
int main() {
try{
double val = divide(10, 0);
cout <<"value: "<< val << endl;
}catch(int e) {
cout << "catch except: " << e << endl;
}
return 0;
}
heheda@heheda:~/Linux/c++11$ g++ -std=c++11 8.noexcept.cpp -o app
heheda@heheda:~/Linux/c++11$ ./app
divide by zero!!!
value: -1