C++异常(1) - 异常介绍

目录

1.为何需要异常处理

2.C++的异常处理

2.1正常流程

2.2捕获所有异常

2.3隐式转换

2.4没有异常捕获

2.5继承类中的捕获顺序

2.6标准异常处理类

2.7异常检查

2.8嵌套捕获

2.9try代码块的析构


相比C语言, C++的一个优势是提供了异常处理机制,定义了相关的关键字。

try: 代表可以抛出异常的代码块。

catch: 用于处理抛出的一个特定异常的代码块。

throw: 用于抛出一个异常。也可以用于一个函数抛出的异常列表。

1.为何需要异常处理

下面是异常处理比传统错误处理的优势。
1. 可以将处理错误的代码与正常代码分离开:
    传统错误处理代码中,通常包含if else条件判断。这些处理错误的条件代码与正常代码会混合在一起。降低了代码的可读性与可维护性。使用try catch代码块,可以将处理异常的代码与正常代码分离开。

2. 函数/方法可以处理他们所选择的任何异常:
    一个函数可以抛出多种异常,但是可以选择只处理其中的一些。另外的一些,可以交由调用者来处理。如果调用者选择不处理这些异常,可以抛给最外层的调用者去处理。
    C++中,一个函数可以使用throw关键字来指定它要抛的异常。此函数的调用者必须采取某些方式来处理异常(或者继续指定异常,或者捕获它)。

3. 错误类型的组合: 
    C++中, 基本类型和对象都能当作异常抛出。我们可以创建异常对象的组合,将异常在namespace或class中分组,按照类型进行分类。

2.C++的异常处理

2.1正常流程

下面是一个简单演示。 输出结果解释了try/catch的执行流程。

#include <iostream>
using namespace std;
int main() {
    int x = -1;

    // Some code
    cout << "Before try \n";
    try {
        cout << "Inside try \n";
        if (x < 0) {
            throw x;
            cout << "After throw (Never executed) \n";
        }
    }
    catch (int x) {
        cout << "Exception Caught \n";
    }

    cout << "After catch (Will be executed) \n";
    return 0;
}

运行结果:
Before try
Inside try
Exception Caught
After catch (Will be executed)

2.2捕获所有异常

有一个特殊的catch代码块,称为'catch call'。catch(...)可以用来捕获所有类型的异常。

例如,下面程序中抛出了一个异常,但是没有catch代码块来捕获int类型,所以catch(...)会捕获并处理。

#include <iostream>
using namespace std;
int main() {
    try {
        throw 10;
    }
    catch (char* excp) {
        cout << "Caught " << excp;
    }
    catch (...) {
        cout << "Default Exception\n";
    }
    return 0;
}

运行结果:
Default Exception

2.3隐式转换

对于原始类型,不会发生隐式转换的情况。

例如,下面代码中的'a'不会隐式转换为int (C++中‘a'代表一个字符,而C中是一个整数)。

#include <iostream>
using namespace std;
int main() {
    try {
        throw 'a';
    }
    catch (int x) {
        cout << "Caught " << x;
    }
    catch (...) {
        cout << "Default Exception\n";
    }
    return 0;
}

运行结果:
Default Exception

2.4没有异常捕获

如果抛出了异常,但是没有任何地方捕获,则程序会异常的终止。

例如,下面程序会终止。

#include <iostream>
using namespace std;
int main() {
    try {
        throw 'a';
    }
    catch (int x) {
        cout << "Caught ";
    }
    return 0;
}

程序运行时,发生异常。

gcc4.8.5+CentOS7.2:
terminate called after throwing an instance of 'char'
Aborted

windows7 + visual studio2015:
abort() has been called

我们可以使用自己定义的异常函数来替换这个异常终止的行为。

2.5继承类中的捕获顺序

子类的异常应该在基类异常之前被捕获。具体可参考本博客的这篇文章《C++异常(3) - 捕获基类与子类的异常》

2.6标准异常处理类

与Java类似, C++库中也定义了一个标准异常处理类,它是所有其它标准类的基类。
标准库中抛出的所有异常对象都是继承自这个类。因此,所有的标准异常都能使用这个类型来进行捕获。

2.7异常检查

不同于Java,C++中所有异常是不检查的。编译器不会检查一个异常是否被捕获。例如函数声明时不需要指出所有不会被捕获的异常,尽管推荐这么做。

例子:下面程序编译正常,但是严格来说fun()应该列出它不会检测的异常。

#include <iostream>
using namespace std;

// 此函数的实现,对于编译器没什么问题,但是不推荐这么做。
// 理论上,此函数应该列出所有不会捕获的异常,并且函数名应该为
// "void fun(int *ptr, int x) throw(int *, int)"。
void fun(int* ptr, int x) {
    if (ptr == NULL)
        throw ptr;
    if (x == 0)
        throw x;
}

int main() {
    try {
        fun(NULL, 0);
    }
    catch (...) {
        cout << "Caught exception from fun()";
    }
    return 0;
}

运行结果:
Caught exception from fun()

2.8嵌套捕获

C++中try-catch代码块是可以嵌套的。另外,一个异常可以使用throw重新抛出。

#include <iostream>
using namespace std;

int main() {
    try {
        try {
            throw 20;
        }
        catch (int n) {
            cout << "Handle partially\n";
            throw;  // 再次抛出异常
        }
    }
    catch (int n) {
        cout << "Handle remaining\n";
    }
    return 0;
}

运行结果:
Handle partially 
Handle remaining

函数中也可以使用throw重新抛出异常。也可以只部分处理,然后请求调用者来处理剩余部分。

2.9try代码块的析构

当抛出了一个异常时。try代码块中创建的所有对象都会被析构掉,在将控制权转移到catch代码块之前。

#include <iostream>
using namespace std;

class Test {
public:
    Test() { cout << "Constructor of Test " << endl; }
    ~Test() { cout << "Destructor of Test " << endl; }
};

int main() {
    try {
        Test t1;
        throw 10;
    }
    catch (int i) {
        cout << "Caught " << i << endl;
    }
}

运行结果:
Constructor of Test
Destructor of Test
Caught 10

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

水草

您的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值