C++异常、I/O流(day12)

一、(续day11异常)

7、标准异常类

  标准异常类在下面的这个路径的头文件中有声明

C++语言本身或者标准库抛出的异常都是 exception 的子类,称为标准异常(Standard Exception)。通过下面的语句来捕获所有的标准异常:

  1. try{
  2. //可能抛出异常的语句
  3. }catch(exception &e){
  4. //处理异常的语句
  5. }

之所以使用引用,是为了提高效率。如果不使用引用,就要经历一次对象拷贝(要调用拷贝构造函数)的过程。

exception 类位于 <exception> 头文件中

 它的声明如下:

class exception{
public:
  exception () throw(); //构造函数
  exception (const exception&) throw(); //拷贝构造函数
  exception& operator= (const exception&) throw(); //运算符重载
  virtual ~exception() throw(); //虚析构函数
  virtual const char* what() const throw(); //虚函数}

这里需要说明的是 what() 函数。what() 函数返回一个能识别异常的字符串,正如它的名字“what”一样,可以粗略地告诉你这是什么异常。不过C++标准并没有规定这个字符串的格式,各个编译器的实现也不同,所以 what() 的返回值仅供参考。

下图展示了 exception 类的继承层次:

C++ exception类层次图
图:exception 类的继承层次以及它们所对应的头文件


先来看一下 exception 类的直接派生类:

异常名称 说  明
logic_error 逻辑错误。
runtime_error 运行时错误。
bad_alloc 使用 new 或 new[ ] 分配内存失败时抛出的异常。
bad_typeid 使用 typeid 操作一个 NULL 指针,而且该指针是带有虚函数的类,这时抛出 bad_typeid 异常。
bad_cast 使用 dynamic_cast 转换失败时抛出的异常。
ios_base::failure io 过程中出现的异常。
bad_exception 这是个特殊的异常,如果函数的异常列表里声明了 bad_exception 异常,当函数内部抛出了异常列表中没有的异常时,如果调用的 unexpected() 函数中抛出了异常,不论什么类型,都会被替换为 bad_exception 类型。
 

 logic_error 的派生类: 

异常名称 说  明
length_error 试图生成一个超出该类型最大长度的对象时抛出该异常,例如 vector 的 resize 操作。
domain_error 参数的值域错误,主要用在数学函数中,例如使用一个负值调用只能操作非负数的函数。
out_of_range 超出有效范围。
invalid_argument 参数不合适。在标准库中,当利用string对象构造 bitset 时,而 string 中的字符不是 0 或1 的时候,抛出该异常。


runtime_error 的派生类: 

异常名称 说  明
range_error 计算结果超出了有意义的值域范围。
overflow_error 算术计算上溢。
underflow_error 算术计算下溢。

例:

 class FileError:public exception{

public:

  const char* what(void) const throw(){

    return "文件访问失败";
  }

};

int main(void){

  try{

    //throw FileError();
    char* p=new char[0xffffffff]

  }

  catch(exception& ex){//使用标准异常类,可以只写一个catch,利用多态就可以完成。

    cout<<ex.what()<<endl;

    return -1;

  }

}

 执行结果:

注意:

  自定义异常类的时候,最好继承标准异常类,并且重写what()函数,让其对标准的what()函数形成覆盖,通过多态的语法特征,在子类的what函数中处理子类异常。

8、构造函数异常

class A{

public:

  A(void){

    cout<<"A::A()"<<endl;

  }

  ~A(void){

    cout<<"A::~A()"<<endl;
  }

};



class B{

public:

  B(void):m_a(new A){

    FILE* fp=fopen("none.txt","r")
    if(!fp){

      delete m_a;//释放不完整类
      throw -1;
    }

    fclose(fp);
  }

  ~B(void){

    delete m_a;
  }

private:

  A* m_a;
};



int main(void){

  try{

    B b;

  }

  catch(int& ex){

    cout<<"异常"<<ex<<endl;
    return -1;

  }

  return 0;

}

注意:

  构造函数抛出异常之后,对象将会被不完整构造,这样的对象其析构函数不会被执行,因此在构造函数抛出异常之前,需要手动销毁所有在异常产生之前的动态资源。

9、析构函数异常

  析构函数最好不要抛出异常(了解)

class A{

public:

  void func(void){

    throw -1;
  }

  ~A(void){

    throw -2;
  }

};
int main(void){

  A a;

  try{

    a.func();
  }

  catch(int& ex){

    cout<<"异常"<<ex<<endl;
    return -1;
  }

}
//因为已经跳出了catch,执行到},析构函数抛出的异常2将永远得不到捕获,最终将被系统所捕获
int main(void){


int main(void){



  try{
    A a;

    a.func();
  }

  catch(int& ex){

    cout<<"异常"<<ex<<endl;
    return -1;
  }

}
//func()抛出的异常还没来及处理,a的析构就抛出异常,这样系统会直接终止进程

二、I/O流(了解)

 
 

猜你喜欢

转载自www.cnblogs.com/ptfe/p/11306608.html