异常概念
1.c语言也有异常,c语言对异常的操作就是段错误,让操作系统干掉他。
2.而异常也可以告诉用户,你想不想要这个异常,如果你想要这个异常,可以捕获他。如果你不捕获,系统就会杀死这个进程。
3.在c语言的世界中,对错误的处理总是围绕两种方法,一是使用整形的返回值标识错误,二是使用errno宏去记录错误。
4.c++异常机制相比c语言异常处理的优势:函数的返回值可以忽略,但异常不可忽略。如果程序出现异常,但是没有被捕获,程序就会被终止。还有就是,整形返回值没有任何语义信息,而异常却包含语义信息,有时从类名就能够体现出来。异常作为一个类,可以拥有自己的成员,这些成员就可以传递足够的信息。
怎么去写异常
在c++中抛出异常,建议按值抛出异常,并使用常量引用捕获它们。
例:
void mayOccurEx(int i)
{
int a = i;
throw a;
}
int main()
{
try {
mayOccurEx(10);
}
catch (const int &e) //使用常量引用来捕获这个异常
{
cout << e << endl;
}
return 0;
}
c++允许您抛出任何类型的异常,尽管通常建议抛出派生自std::exception的类型。C++异常可以由与抛出异常相同类型的catch表达式捕获,或者由可以捕获任何类型异常的catch表达式捕获。如果抛出的异常类型是一个类,它也有一个基类或多个子类,那么接收这个类型或这个类型基类的引用可以捕获这个异常。请注意,当异常被引用捕获,它会绑定到实际抛出的异常对象。如果不是一个引用,他就是实际抛出的异常对象的拷贝(和函数的参数基本相同)。
抛出异常时,可能会被以下类型的捕获表达式捕获:
1. catch(...)
2. catch(与抛出异常对象类型相同 e),忽略const和volatile修饰符
3. catch(与抛出异常对象类型相同的引用类型 &e)
4. catch(带const或volatile且与抛出异常对象类型相同的引用类型 &e)
5. catch(与抛出异常对象基类类型相同的类型 e),忽略const和volatile修饰符。注意:基类的捕获表达式不能位于派生类捕获表达式之前
6. catch(与抛出异常对象基类类型相同的引用类型 &e)
7. catch(带const或volatile且与抛出异常对象基类类型相同的引用类型 &e)
8. catch(抛出指针或其基类类型的指针,通过标准指针转换规则可以将抛出的指针对象转换为接收的指针)
注意:找到匹配的catch表达式后,不会检查后续处理程序
当使用/EHa选项编译时,catch(...)处理任何一种异常(包括结构化异常和系统生成或应用程序生成的异步异常)。处理异常时有一个重要技巧,除非catch块知道如何处理捕获的特定异常,否则不要允许程序继续。通常,catch(...)用于记录错误,并在程序停止执行之前执行特殊清理。没有操作数的throw会重新抛出当前正在处理的异常。建议在重新引发异常时使用这种形式。因为它保留了原始异常的多种类型信息。这样的表达式只能在catch块中或在catch块中的函数内使用。重新抛出的异常对象是原始异常对象,而不是拷贝。
std::exception
std::exception是每一个标准异常类的基类,c++的标准库抛出的所有异常对象都派生自std::exception。因此,通过catch(const exception& e)或catch(exception &e)可以捕获所有标准异常。因为c++中的异常抛出的时候会调用拷贝构造函数,所以每个异常类都至少有一个拷贝构造函数。例如:
struct ex :public exception
{
const char* what() const noexcept { return "ex oOps!\n"; }
};
int main() {
ex e;
exception * p = &e;
try {
throw e; //调用的拷贝构造函数是 ex(e)
}
catch (const exception&e)
{
cout << e.what() << endl; //打印 ex oOps!
}
try {
throw *p; //调用的拷贝构造函数是 exception(*p)
}
catch (const exception&e)
{
cout << e.what() << endl; //打印UnKnow exception!
}
return 0;
}
std::exception的派生类
bad_alloc:分配内存失败时,引发bad_alloc异常。即使用operator new和operator new[]的标准定义未能分配请求的存储空间时引发该异常类型。
bad_cast:dynamic_cast<Derived &>(base对象),引发bad_cast异常。抛出这种类型的异常,也可能标识着类型转换错误!
class Base { virtual void member() {} };
class Derived : Base {};
int main() {
try
{
Base b;
Derived& rd = dynamic_cast<Derived&>(b);
}
catch (std::bad_cast& bc)
{
std::cerr << "bad_cast caught: " << bc.what() << '\n';
}
return 0;
}
bad_exception:一种特殊类型的异常,在函数抛出异常说明符列出(c++11不推荐使用抛出异常说明符)。从c++11开始,抛出此异常表示复制异常对象的尝试失败
void myunexpected() {
std::cerr << "unexpected handler called\n";
throw;
}
void myfunction() throw (int, std::bad_exception) {
throw 'x'; // 抛出char类型的异常,没在异常说明符中列出
}
int main(void) {
std::set_unexpected(myunexpected);
try {
myfunction();
}
catch (int) { std::cerr << "caught int\n"; }
catch (std::bad_exception be) { std::cerr << "caught bad_exception\n"; }
catch (...) { std::cerr << "caught some other exception\n"; }
return 0;
}
bad_function_call:调用未赋值的std::function函数对象时抛出次异常
int main(void) {
function<int(int, int)>bar;
try {
bar(1, 0);
}
catch (const bad_function_call& e)
{
cout << e.what() << endl;
}
return 0;
}
bad_typeid:typeid(*多态对象指针),当指针指向NULL,抛出此异常
bad_alloc *bae = NULL;
try {
typeid(*bae).name();
}
catch (const bad_typeid&bt_id)
{
cout << bt_id.what() << endl;
}
bad_weak_ptr:当使用过期的weak_ptr构造shared_ptr抛出此异常
logic_error:程序逻辑中的错误,这些错误在程序执行之前可能是可以检测到的。它用作几个逻辑错误异常的基类!它的同级类runtime_error报告只能在运行时确定的错误的异常
class logic_error : public exception {
public:
explicit logic_error (const string& what_arg);
explicit logic_error (const char* what_arg);
};
runtime_error:报告只能在运行时检测到的错误
class runtime_error : public exception {
public:
explicit runtime_error (const string& what_arg);
explicit runtime_error (const char* what_arg);
};

domain_error:例如,数学函数的domain,因为平凡根函数只为非负数定义。因此,此类函数的负数,就符合domain_error的条件
future_error:对future对象和其他可能访问future的共享状态的无效操作,会引发此异常
invalid_argument:参数不合适。在标准库中,当利用string对象构造bitset时,而string中的字符不是"0"或"1"的时候,抛出该异常
length_error:当某些容器需要调整大小,传的参数大于其能够调整大小的范围,会报此异常
#include <stdexcept>
vector<int> v;
try {
v.resize(v.max_size() + 1);
}
catch (const length_error & le)
{
cout << le.what() << endl;
}
out_of_range:当参数超出范围,会抛出此异常.

range_error:计算结果超出了有意义的值域范围。当范围错误,会抛此异常
underflow_error:算术下溢,而overflow是算术上溢
栈解旋
异常被抛出后,从进入try块起,到异常被抛出前,这期间在栈上构造的所有对象,都会被自动析构。析构的顺序与构造的顺序相反,这一过程称为栈的解旋。