C++异常总结

异常概念

  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_castdynamic_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);
};

logic_error类图

  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:当参数超出范围,会抛出此异常.

runtime_error类图

  range_error:计算结果超出了有意义的值域范围。当范围错误,会抛此异常
  underflow_error:算术下溢,而overflow是算术上溢

栈解旋

  异常被抛出后,从进入try块起,到异常被抛出前,这期间在栈上构造的所有对象,都会被自动析构。析构的顺序与构造的顺序相反,这一过程称为栈的解旋。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

友情链接更多精彩内容