63 C++ - 异常语法

简介: 63 C++ - 异常语法

1. 异常基本语法

int A_MyDivide(int a, int b){
  if (b == 0){
    throw 0;
  }
  return a / b;
}
//B写的代码 B写代码比较粗心,忘记处理异常
int B_MyDivide(int a, int b){
  int ba = a;
  int bb = b;
  int ret = A_MyDivide(ba, bb) + 100;  //由于B没有处理异常,导致B结果运算错误
  return ret;
}
//C写的代码
int C_MyDivide(){
  int a = 10;
  int b = 0;
  int ret = 0;
//没有处理异常,程序直接中断执行
#if 1 
  ret = B_MyDivide(a, b);
//处理异常
#else 
  try{
    ret = B_MyDivide(a, b); //更严重的是,由于B没有继续抛出异常,导致C的代码没有办法捕获异常
  }
  catch (int e){
    cout << "C_MyDivide Call B_MyDivide 除数为:" << e << endl;
  }
#endif
  return ret;
}
int main(){
  C_MyDivide();
  system("pause");
  return EXIT_SUCCESS;
}

总结:

  • 若有异常则通过throw操作创建一个异常对象并抛出。
  • 将可能抛出异常的程序段放到try块之中。
  • 如果在try段执行期间没有引起异常,那么跟在try后面的catch字句就不会执行。
  • catch子句会根据出现的先后顺序被检查,匹配的catch语句捕获并处理异常(或继续抛出异常)
  • 如果匹配的处理未找到,则运行函数terminate将自动被调用,其缺省功能调用abort终止程序。
  • 处理不了的异常,可以在catch的最后一个分支,使用throw,向上抛。

c++异常处理使得异常的引发和异常的处理不必在一个函数中,这样底层的函数可以着重解决具体问题,而不必过多的考虑异常的处理。上层调用者可以在适当的位置设计对不同类型异常的处理。

2. 异常严格类型匹配

异常机制和函数机制互不干涉,但是捕捉方式是通过严格类型匹配。

void TestFunction(){
  cout << "开始抛出异常..." << endl;
  //throw 10; //抛出int类型异常
  //throw 'a'; //抛出char类型异常
  //throw "abcd"; //抛出char*类型异常
  string ex = "string exception!";
  throw ex;
}
int main(){
  try{
    TestFunction();
  }
  catch (int){
    cout << "抛出Int类型异常!" << endl;
  }
  catch (char){
    cout << "抛出Char类型异常!" << endl;
  }
  catch (char*){
    cout << "抛出Char*类型异常!" << endl;
  }
  catch (string){
    cout << "抛出string类型异常!" << endl;
  }
  //捕获所有异常
  catch (...){
    cout << "抛出其他类型异常!" << endl;
  }
  system("pause");
  return EXIT_SUCCESS;
}

3. 栈解旋(unwinding)

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

class Person{
public:
  Person(string name){
    mName = name;
    cout << mName << "对象被创建!" << endl;
  }
  ~Person(){
    cout << mName << "对象被析构!" << endl;
  }
public:
  string mName;
};
void TestFunction(){
  Person p1("aaa");
  Person p2("bbb");
  Person p3("ccc");
  //抛出异常
  throw 10;
}
int main(){
  try{
    TestFunction();
  }
  catch (...){
    cout << "异常被捕获!" << endl;
  }
  system("pause");
  return EXIT_SUCCESS;
}

4. 异常接口声明

为了加强程序的可读性,可以在函数声明中列出可能抛出异常的所有类型,例如:void func() throw(A,B,C);这个函数func能够且只能抛出类型A,B,C及其子类型的异常。

如果在函数声明中没有包含异常接口声明,则此函数可以抛任何类型的异常,例如:void func()

一个不抛任何类型异常的函数可声明为:void func() throw()

如果一个函数抛出了它的异常接口声明所不允许抛出的异常,unexcepted函数会被调用,该函数默认行为调用terminate函数中断程序。

//可抛出所有类型异常
void TestFunction01(){
  throw 10;
}
//只能抛出int char char*类型异常
void TestFunction02() throw(int,char,char*){
  string exception = "error!";
  throw exception;
}
//不能抛出任何类型异常
void TestFunction03() throw(){
  throw 10;
}
int main(){
  try{
    //TestFunction01();
    //TestFunction02();
    //TestFunction03();
  }
  catch (...){
    cout << "捕获异常!" << endl;
  }
  system("pause");
  return EXIT_SUCCESS;
}

请分别在qt vs linux下做测试! Qt and Linux 正确!

5. 异常变量生命周期

throw的异常是有类型的,可以是数字、字符串、类对象。

throw的异常是有类型的,catch需严格匹配异常类型。

class MyException
{
public:
  MyException(){
    cout << "异常变量构造" << endl;
  };
  MyException(const MyException & e)
  {
    cout << "拷贝构造" << endl;
  }
  ~MyException()
  {
    cout << "异常变量析构" << endl;
  }
};
void DoWork()
{
  throw new MyException(); //test1 2都用 throw MyExecption();
}
void test01()
{
  try
  {
    DoWork();
  }
  catch (MyException e)
  {
    cout << "捕获 异常" << endl;
  }
}
void test02()
{
  try
  {
    DoWork();
  }
  catch (MyException &e)
  {
    cout << "捕获 异常" << endl;
  }
}
void test03()
{
  try
  {
    DoWork();
  }
  catch (MyException *e)
  {
    cout << "捕获 异常" << endl;
    delete e;
  }
}

6. 异常的多态使用

//异常基类
class BaseException{
public:
  virtual void printError(){};
};
//空指针异常
class NullPointerException : public BaseException{
public:
  virtual void printError(){
    cout << "空指针异常!" << endl;
  }
};
//越界异常
class OutOfRangeException : public BaseException{
public:
  virtual void printError(){
    cout << "越界异常!" << endl;
  }
};
void doWork(){
  throw NullPointerException();
}
void test()
{
  try{
    doWork();
  }
  catch (BaseException& ex){
    ex.printError();
  }
}


目录
相关文章
|
2月前
|
Java C# C++
C++ 11新特性之语法甜点1
C++ 11新特性之语法甜点1
33 4
|
2月前
|
编译器 C++ 容器
C++ 11新特性之语法甜点2
C++ 11新特性之语法甜点2
30 1
|
2月前
|
存储 算法 编译器
C++ 11新特性之语法甜点4
C++ 11新特性之语法甜点4
28 0
|
2月前
|
安全 C++ 容器
C++ 11新特性之语法甜点3
C++ 11新特性之语法甜点3
35 0
|
3月前
|
编译器 C++ 容器
C++语言的基本语法
想掌握一门编程语言,第一步就是需要熟悉基本的环境,然后就是最重要的语法知识。 C++ 程序可以定义为对象的集合,这些对象通过调用彼此的方法进行交互。现在让我们简要地看一下什么是类、对象,方法、即时变量。 对象 - 对象具有状态和行为。例如:一只狗的状态 - 颜色、名称、品种,行为 - 摇动、叫唤、吃。对象是类的实例。 类 - 类可以定义为描述对象行为/状态的模板/蓝图。 方法 - 从基本上说,一个方法表示一种行为。一个类可以包含多个方法。可以在方法中写入逻辑、操作数据以及执行所有的动作。 即时变量 - 每个对象都有其独特的即时变量。对象的状态是由这些即时变量的值创建的。 完整关键字
|
4月前
|
C++
C++ 异常机制问题之捕获异常的问题如何解决
C++ 异常机制问题之捕获异常的问题如何解决
|
4月前
|
Java 编译器 程序员
C++中的语法知识虚继承和虚基类
**C++中的多继承可能导致命名冲突和数据冗余,尤其在菱形继承中。为解决这一问题,C++引入了虚继承(virtual inheritance),确保派生类只保留虚基类的一份实例,消除二义性。虚继承通过`virtual`关键字指定,允许明确访问特定路径上的成员,如`B::m_a`或`C::m_a`。这样,即使基类在继承链中多次出现,也只有一份成员副本,简化了内存布局并避免冲突。虚继承应在需要时提前在继承声明中指定,影响到从虚基类派生的所有后代类。**
|
4月前
|
安全 Java 程序员
【C++11】异常知多少
【C++11】异常知多少
41 7
|
4月前
|
编译器 C++ 开发者
C++一分钟之-属性(attributes)与属性语法
【7月更文挑战第3天】C++的属性(attributes)自C++11起允许附加编译器指令,如`[[nodiscard]]`和`[[maybe_unused]]`,影响优化和警告。注意属性放置、兼容性和适度使用,以确保代码清晰和可移植。示例展示了如何使用属性来提示编译器处理返回值和未使用变量,以及利用编译器扩展进行自动清理。属性是提升代码质量的工具,但应谨慎使用。
140 13
|
5月前
|
编译器 程序员 C++
C++一分钟之-属性(attributed)与属性语法
【6月更文挑战第28天】C++的属性为代码添加元数据,帮助编译器理解意图。C++11引入属性语法`[[attribute]]`,但支持取决于编译器。常见属性如`nodiscard`提示检查返回值,`maybe_unused`防止未使用警告。问题包括兼容性、过度依赖和误用。使用属性时需谨慎,确保团队共识,适时更新以适应C++新特性。通过示例展示了`nodiscard`和`likely/unlikely`的用法,强调正确使用属性能提升代码质量和性能。
84 13