目录
一.异常
1.1C++异常概念
1.2异常的使用
1.3异常和栈帧,重新抛出
二.异常体系
2.1自定义异常体系
2.2C++标准库的异常体系
2.3异常规范
3.异常的优缺点
3.1优点
3.2缺点
double Division(int a, int b)
{// 当b == 0时抛出异常if (b == 0)throw "Division by zero condition!";elsereturn ((double)a / (double)b);
}
void Func()
{int a, b;cin >> a >> b;cout << Division(a,b) << endl;
}
int main()
{try{Func();}catch (const char* errmsg){cout << errmsg << endl;}catch (...){cout << "unkown exception" << endl;}return 0;
}
当发生除零错误时会被抛出,被catch捕获,catch(...)可以捕获任意类型的异常,但会优先去匹配最适合的。
如图
也就是要去匹配最适合的catch语句。
有可能单个的catch不能完全处理一个异常,在进行一些校正处理以后,希望再交给更外层的调用 链函数来处理,catch则可以通过重新抛出将异常传递给更上层的函数进行处理。
例如:(例子不是很恰当)
double Division(int a, int b)
{// 当b == 0时抛出异常if (b == 0)throw "Division by zero condition!";elsereturn ((double)a / (double)b);
}
void Func()
{int a, b;cin >> a >> b;cout << Division(a,b) << endl;
}
void show()
{int* arr = new int[10];Func();delete[] arr;
}
int main()
{try{show();}catch (const char* errmsg){cout << errmsg << endl;}catch (...){cout << "unkown exception" << endl;}return 0;
}
当发生除零错误时,通过throw直接跳到catch语句后,往后执行,而show()函数内new 出来的空间未释放。
解决办法:
void show()
{int* arr = new int[10];try {Func();//若Func函数出现异常,将前面的arr释放}catch (...){delete[]arr;throw; //重新抛出}delete[] arr;
}
可以定义一套继承的规范异常体系,抛出继承的派生类,捕获一个基类即可。
例如:
定义一个父类,两个子类。发生异常,抛出子类,在子类中填充错误信息,用父类接受,通过多态实现,打印错误信息。
class A
{
public:A(const string str, int id):str_(str),id_(id){}virtual string what() const{return str_;}string str_;int id_;
};class Aa : public A
{
public:Aa(string str,int id,string stra):stra_(stra),A(str,id){}virtual string what() const{string Str("Aa");Str += stra_;return Str;}
private:string stra_;
};class Ab :public A
{
public:Ab(string str, int id, string strb):strb_(strb),A(str,id){}virtual string what() const{string Str("Ab");Str += strb_;return Str;}
private:string strb_;
};void AAA()
{//若发生错误throw Aa("AAA", 8, "AAAA");
}void BBB()
{//若发生错误throw Ab("BBB", 9, "BBBB");
}
void show()
{AAA();BBB();
}
int main()
{try {show();}catch (A& a){cout << a.what() << endl;}return 0;
}
结果:
C++ 提供了一系列标准的异常,我们可以在程序中使用这些标准的异常。它们是以父子类层次结构组织起来的。
① 在上述继承体系中,每个类都有提供了构造函数、复制构造函数、和赋值操作符重载。
② logic_error类及其子类、runtime_error类及其子类,它们的构造函数是接受一个string类型的形式参数,用于异常信息的描述
③ 所有的异常类都有一个what()方法,返回const char* 类型(C风格字符串)的值,描述异常信息。
对异常的一写说明:
简单看个例子:
1.在函数后加上throw(类型A,类型B)
可以列出这个函数能抛出的所有异常类型
void test() throw(string,vector);
2,如果只跟一个类型,代表该函数只会抛出一种类型的异常
void* operator new(size_t size) throw (std::bad_alloc);
3.如果跟的是throw()
代表这个函数不会抛出异常
void* test(size_t sz, void* p) throw();
4.noexcept
来标识不会抛出异常
void* test(size_t sz, void* p) noexcept;
例如:
1.将异常对象定义好后,相比错误码的方式可以清晰准确的展示出错误的各种信息,更加直观。
2.传统返回错误码的方式去返回错误码时,需要层层返回。
3.很多的第三方库都包含异常,比如boost、gtest等等常用的库,那么我们使用它们时也需要使用异常。
4. 部分函数使用异常更好处理,比如构造函数没有返回值,不方便使用错误码方式处理。比如 T& operator这样的函数,如果pos越界了只能使用异常或者终止程序处理,没办法通过返回 值表示错误。
1.异常的执行流跳动比较大,有时可能很难追踪错误。
2.C++没有垃圾回收机制,异常非常容易导致内存泄漏、死锁等异常安全问题。(使用RAII来处理资源的管理问题)。
3.异常需要规范去使用,依赖用户的规范,习惯。
上一篇:【深度学习】详解 BEiT