frankfan的胡思乱想

学海无涯,回头是岸

异常

异常

本章讲授的主要内容是『异常』。在软件开发中,异常的产生是不可避免的,不合法的内存地址值,不合法的运算值,错误的函数参数等都可能产生软件异常。

出现了异常最直接的表现为应用退出,这在某些场景下是可以接受的,因为这暴露了软件的bug,可以及时修复,而在某些场景是是不可被接受的,一些高稳定要求场景下是不允许软件退出的,此时这个异常就必须被捕获,并且被合理处理。

其实关于软件异常是否该被捕获,或者是否应该使用错误码的方式代替异常处理等一直都有争论,各有理由。

其实C++中的异常是否应该使用也有不同的声音,C++中异常的使用会导致编译后代码膨胀,甚至导致性能下降,在某些场合下甚至鼓励通过应用闪退的手段来暴露软件问题。

不过目前支持在C++中使用异常的支持者要稍占上风。

异常的产生

在软件开发中,设计函数的时候,函数的逻辑本身是正确的,但是却可能隐藏各种意想不到的运行时错误,这种错误通常通常不符合调用者的预期,因此函数本身需要通过某种机制反馈给函数调用者,让调用者方便处理属于他自己的逻辑。(至于此时调用者是决定退出软件还是弹出提醒或者置之不理等是属于函数调用者的业务需求了,与函数本身无关)

#include <iostream>
int Add(int a,int b){
  return a + b;
}

这是一个简单直白的加法函数,逻辑没有问题。但是却存在整型溢出的风险。

也就是说这个函数调用会产生2种不同的情况。

1、没有溢出,返回值正确返回。

2、整型溢出,返回值出错。

因此,需要能够正确的返回2种不同的情况拱函数调用者使用。

可以通过函数参数(指针类型参数)作为信息传递桥梁,但这种方式对函数调用者及不友好,也多有局限,因此 异常机制应运而生

异常

#include <iostream>
using namespace std;

void handleData(int v){
  //do something...
  //此时出现异常情况,将该情况『抛出』交给外界调用者
  throw 1/"错误"/...//可以抛出任何类型的值和类
}

int main(){
  try{
    //正常执行函数逻辑
    handleData(11);
    
    //若handleData函数调用时产生异常,下面这句代码并不会执行
    cout<<"handleData"<<endl;
    
  }catch(int v){
    //当函数执行出错,会直接进入这个代码块
  }
  
  //无论函数调用是否产生异常,下面这句代码都会被执行
  cout<<"end"<<endl;
  return 0;
}

可以捕获不同类型的信息

#include <iostream>
using namespace std;

void handleData(int v){
  //do something...
  //此时出现异常情况,将该情况『抛出』交给外界调用者
  throw 1/"错误"/...//可以抛出任何类型的值和类
}

int main(){
  try{
    handleData(11);
  }catch(int v){
    //当函数执行出错,抛出的数据类型是整型时,会直接进入这个代码块
  }catch(float v){
    //当函数执行出错,抛出的数据类型是浮点型时,会直接进入这个代码块
  }catch(const char *v){
    //当函数执行出错,抛出的数据类型是const char*型时,会直接进入这个代码块
  }
  
	//catch抛出信息时,会严格遵守数据类型,并不会产生隐式转化
  return 0;
}

显然,上述的各种catch实在是太过啰嗦,此时用C++提供的『万能捕获』

#include <iostream>
using namespace std;

void handleData(int v){
  //do something...
  //此时出现异常情况,将该情况『抛出』交给外界调用者
  throw 1/"错误"/...//可以抛出任何类型的值和类
}

int main(){
  try{
    handleData(11);
  }catch(...){
		//当异常抛出时,无论其是什么类型,都能进这个代码块
  }
  return 0;
}

被抛出的异常信息若是一个单纯的数据,那么能够表达的信息十分受限,比如不管是抛出一个整型错误码还是字符串的错误消息,外界能够获取到的信息都是不足的。

直接抛出一个自定义的类即可

#include <iostream>
using namespace std;

class Baseexception{
public:
  
  Baseexception(string emsg,int ecode){
    err_msg = emsg;
    err_code = ecode;
  }
  void what(){
    cout<<err_msg<<" "<<"error code is "<<err_code<<endl;
  }
private:
  string err_msg;
  int err_code;
};

void handleData(int v){
  
  //do something...
  throw Baseexception("some error occur",404);
}

int main(){
	try{
      handleData(11);
  }catch(Baseexception bc){
	    bc.what();
  }
  return 0;
}

异常抛出,不论函数调用嵌套的层级有多深,都能一级一级往上进行异常传递,直到被捕获或者应用退出。

#include <iostream>
using namespace std;

void innerfunc(){
  throw 404;
}

void func(){
  //
  innerfunc();
}

void func1(){
  func();
}

int main(){
  try{
    func1();
  }catch(...){
    //无论函数嵌套调用多深,最外层都能捕捉到抛出的异常
  }
  return 0;
}

使用异常需要认识到

1、一旦调用throw,则代码执行流程立即跳出

2、只要没捕获异常,无论异常抛出多深,最终最外层都能正常捕获

3、类的构造与析构函数中不要抛异常

posted on 2021-12-28 00:15  shadow_fan  阅读(34)  评论(0编辑  收藏  举报

导航