二、栈解旋
异常被抛出后,从进入 try 语句块开始,到异常被抛出之前,这期间在栈上构造的所有对象,都会被
自动析构。析构的顺序与构造的顺序相反。这一过程称为栈的解旋。
也就是在执行 throw 前,在 try 执行期间构造的所有对象被自动析构后,才会进入 catch 匹配。
在堆上构造的对象肿么办?
三、异常规范
C++98 标准提出了异常规范,目的是为了让使用者知道函数可能会引发哪些异常。
void func1() throw(A, B, C); // 表示该函数可能会抛出 A、B、C 类型的异常。
void func2() throw(); // 表示该函数不会抛出异常。
void func3(); // 该函数不符合 C++98 的异常规范。
C++11 标准弃用了异常规范,使用新增的关键字 noexcept 指出函数不会引发异常。
void func4() noexcept; // 该函数不会抛出异常。
在实际开发中,大部分程序员懒得在函数后面加 noexcept,弃用异常已是共识,没必要多此一举。
关键字 noexcept 也可以用作运算符,判断表达试(操作数)是否可能引发异常;如果表达式可能引
发异常,则返回 false,否则返回 true。 四、C++标准库异常
五、重点关注的异常
1)std::bad_alloc
如果内存不足,调用 new 会产生异常,导致程序中止;如果在 new 关键字后面加(std::nothrow)选
项,则返回 nullptr,不会产生异常。
示例:
#include <iostream>
using namespace std;
int main()
{
try {
// 如果分配内存失败,会抛出异常。
//double* ptr = new double[100000000000];
// 如果分配内存失败,将返回 nullptr,会抛出异常。
double* ptr = new (std::nothrow) double[100000000000];
if (ptr == nullptr) cout << "ptr is null.\n";
}
catch (bad_alloc& e)
{
cout << "catch bad_alloc.\n";
}
}
2)std::bad_cast
dynamic_cast 可以用于引用,但是,C++没有与空指针对应的引用值,如果转换请求不正确,会出
现 std::bad_cast 异常。
3)std::bad_typeid
假设有表达式 typeid(*ptr),当 ptr 是空指针时,如果 ptr 是多态的类型,将引发 std::bad_typeid
异常。 六、逻辑错误异常
程序的逻辑错误产生的异常 std::logic_error,通过合理的编程可以避免。
1)std::out_of_range
Defines a type of object to be thrown as exception. It reports errors that are consequ
ence of attempt to access elements out of defined range. It may be thrown by the member functions of std::bitset and std::basic_string, by std::
stoi and std::stod families of functions, and by the bounds-checked member access functi
ons (e.g. std::vector::at and std::map::at). 2)std::length_error
Defines a type of object to be thrown as exception. It reports errors that result from
attempts to exceed implementation defined length limits for some object. This exception is thrown by member functions of std::basic_string and std::vector::rese
rve.3)std::domain_error
Defines a type of object to be thrown as exception. It may be used by the implemen
tation to report domain errors, that is, situations where the inputs are outside of the do
main on which an operation is defined. The standard library components do not throw this exception (mathematical functions
report domain errors as specified in math_errhandling). Third-party libraries, however, use
this. For example, boost.math throws std::domain_error if boost::math::policies::throw_on_err
or is enabled (the default setting). 4)std::invalid_argument
Defines a type of object to be thrown as exception. It reports errors that arise becaus
e an argument value has not been accepted. This exception is thrown by std::bitset::bitset, and the std::stoi and std::stof families of
functions. 示例 1:
#include <iostream>
#include <vector>
using namespace std;
int main()
{
try{
vector<int> vv = { 1,2,3 }; // 容器 vv 中只有三个元素。
vv.at(3) = 5; // 将引发 out_of_range 异常。
}
catch (out_of_range) {
cout << "出现了 out_of_range 异常。\n";
}
}
示例 2:
#include <stdexcept>
#include <iostream>
#include <string>
using namespace std;
int main()
{
string str = "123"; // 不会抛出异常。
//string str = ""; // 将抛出 Invalid_argument 异常。
//string str = "253647586946334221002101"; // 将抛出 out_of_range 异常。
try {
int x = stoi(str); // 把 string 字符串转换为整数。
cout << "x=" << x << endl;
}
catch (invalid_argument&) {
cout << " invalid_argument. \n";
}
catch (out_of_range&) {
cout << " out of range. \n";
}
catch (...) {
cout << " something else…" << endl;
}
}
七、其它异常
1)std::range_error
Defines a type of object to be thrown as exception. It can be used to report range e
rrors (that is, situations where a result of a computation cannot be represented by the d
estination type). The only standard library components that throw this exception are std::wstring_conv
ert::from_bytes and std::wstring_convert::to_bytes. The mathematical functions in the standard library components do not throw this exc
eption (mathematical functions report range errors as specified in math_errhandling). 2)std::overflow_error
Defines a type of object to be thrown as exception. It can be used to report arithmet
ic overflow errors (that is, situations where a result of a computation is too large for the
destination type)
The only standard library components that throw this exception are std::bitset::to_ulo
ng and std::bitset::to_ullong. The mathematical functions of the standard library components do not throw this exc
eption (mathematical functions report overflow errors as specified in math_errhandling). Th
ird-party libraries, however, use this. For example, boost.math throws std::overflow_error if b
oost::math::policies::throw_on_error is enabled (the default setting). 3)std::underflow_error
Defines a type of object to be thrown as exception. It may be used to report arithme
tic underflow errors (that is, situations where the result of a computation is a subnormal
floating-point value)
The standard library components do not throw this exception (mathematical functions
report underflow errors as specified in math_errhandling). Third-party libraries, however, u
se this. For example, boost.math throws std::underflow_error if boost::math::policies::throw_o
n_error is enabled (the default setting
4)ios_base::failure
这个异常,程序员不主动找它就没事。
示例:
#include <iostream>
#include <fstream>
int main()
{
using namespace std;
fstream file;
file.exceptions(ios::failbit); // 设置如果出现 ios::failbit,就引发异常。
try
{
file.open("rm.txt", ios_base::in); // 如果打开的文件不存在,就会引发异常。
}
catch (ios_base::failure f)
{
cout << caught an exception: " << f.what() << endl;
}
}
5)std::bad_exception
This is a special type of exception specifically designed to be listed in the dynamic-ex
ception-specifier of a function (i.e., in its throw specifier).
If a function with bad_exception listed in its dynamic-exception-specifier throws an exce
ption not listed in it and unexpected rethrows it (or throws any other exception also not i
n the dynamic-exception-specifier), a bad_exception is automatically thrown. 210、C++断言
一、断言
断言(assertion)是一种常用的编程手段,用于排除程序中不应该出现的逻辑错误。
使用断言需要包含头文件<cassert>或<assert.h>,头文件中提供了带参数的宏 assert,用于程序在
运行时进行断言。
语法:assert(表达式);
断言就是判断(表达式)的值,如果为 0(false),程序将调用 abort()函数中止,如果为非 0(true),
程序继续执行。
断言可以提高程序的可读性,帮助程序员定位违反了某些前提条件的错误。
注意:
断言用于处理程序中不应该发生的错误,而非逻辑上可能会发生的错误。
不要把需要执行的代码放到断言的表达式中。
断言的代码一般放在函数/成员函数的第一行,表达式多为函数的形参。
示例:
#include <iostream>
#include <cassert> // 断言 assert 宏需要包含的头文件。
using namespace std;
void copydata(void *ptr1,void *ptr2) // 把 ptr2 中的数据复制到 ptr1 中。
{
assert(ptr1&&ptr2); // 断言 ptr1 和 ptr2 都不会为空。
cout << "继续执行复制数据的代码......\n";
}
int main()
{
int ii=0,jj=0;
copydata(&ii, &jj); // 把 ptr2 中的数据复制到 ptr1 中。
}