【C++深度解析】47、C++ 中的异常处理(下)

1 catch 将捕获的异常抛出

  • catch 中捕获的异常可以被重新解释后抛出
    在这里插入图片描述
    catch 可以捕获异常,也可以重新抛出,抛出的异常被外层的 catch 捕获。
void Demo()
{
    try
    {
        try
        {
            throw 'c';
        }
        catch(int i)
        {
            cout << "Inter: catch(int i)" << endl;
            throw i;
        }
        catch(...)
        {
            cout << "Inter: catch(...)" << endl;
            throw;
        }
    }
    catch(...)				// 内层抛出的异常被外层catch捕获
    {
        cout << "other: catch(...)" << endl;
    }
}

为什么要在 catch 语句块中重新抛出异常呢。三方库中很多异常类型为 int,我们希望将重新定义异常类型,将捕获的 int 异常类型进行重定义,然后重新抛出

// 47-1.cpp
#include<iostream>
using namespace std;
/*
假设: 函数func是三方库中的函数,我们无法修改源代码
函数名: void func(int i)
抛出异常的类型: int
		-1 ==》 参数异常
		-2 ==》 运行异常
		-3 ==》 超时异常
*/
void func(int i)
{
    if (i < 0)
    {
        throw -1;
    }
    if (i > 100)
    {
        throw -2;
    }
    if (i == 11)
    {
        throw -3;
    }
    cout << "Run func..." << endl;
}

void MyFunc(int i)
{
    try
    {
        func(i);
    }
    catch(int i)
    {
        switch(i)
        {
            case -1:
                throw "Invalid Parameter";
                break;
            case -2:
                throw "Runtime Exception";
                break;
            case -3:
                throw "Timeout Exception";
                break;
        }
    }
}

int main()
{
    try
    {
        MyFunc(11);
    }
    catch(const char* cs)
    {
        cout << "Exception Info: " << cs << endl;
    }
    return 0;
}

这里我们假设函数 func 是库函数,抛出的异常为 int 类型,我们在函数 MyFunc 中捕获,重新定义异常再抛出。这样就可以捕获我们自己定义的异常了。

编译运行:

$ g++ 47-1.cpp -o 47-1
$ ./47-1
Exception Info: Timeout Exception

2 类类型的异常

  • 异常类型可以是自定义类类型
  • 对于类类型异常的匹配依旧是至上而下严格匹配
  • 赋值兼容性原则在异常匹配中依然适用,所以
    • 匹配子类异常的 catch 放在上部
    • 匹配父类异常的 catch 放在下部
  • 在定义 catch 语句时推荐使用引用作为参数

编程实验:类类型的异常

// 47-2.cpp
#include<iostream>
using namespace std;
class Base
{
};
class Exception : public Base
{
    int m_id;
    string m_desc;
public:
    Exception(int id, string desc)
    {
        m_id = id;
        m_desc = desc;
    }
    int id() const
    {
        return m_id;
    }
    string description() const
    {
        return m_desc;
    }
};
/*
假设: 函数func是三方库中的函数,我们无法修改源代码
函数名: void func(int i)
抛出异常的类型: int
		-1 ==》 参数异常
		-2 ==》 运行异常
		-3 ==》 超时异常
*/
void func(int i)
{
    if (i < 0)
    {
        throw -1;
    }
    if (i > 100)
    {
        throw -2;
    }
    if (i == 11)
    {
        throw -3;
    }
    cout << "Run func..." << endl;
}

void MyFunc(int i)
{
    try
    {
        func(i);
    }
    catch(int i)
    {
        switch(i)
        {
            case -1:
                throw Exception(-1, "Invalid Parameter");
                break;
            case -2:
                throw Exception(-2, "Runtime Exception");
                break;
            case -3:
                throw Exception(-3, "Timeout Exception");
                break;
        }
    }
}
int main()
{
    try
    {
        MyFunc(11);
    }
    // 类类型异常捕获用引用,避免拷贝
    // 先捕获子类异常,后捕获父类异常
    catch(const Exception& e)				// 先捕获子类异常
    {
        cout << "Exception Info: " << endl;
        cout << "    ID: " << e.id() << endl;
        cout << "    m_desc: " << e.description() << endl;
    }
    catch(const Base& e)					// 后捕获父类异常
    {
        cout << "catch(const Base& e)" << endl;
    }
    return 0;
}

我们定义了异常类 Exception(继承 Base),MyFunc 函数 catch 语句将捕获的异常重新包装成类类型,再抛出,捕获类类型的异常时,由于赋值兼容性原则,子类对象可以转换为父类对象,所以要先捕获子类对象,再捕获父类对象。

编译运行:

$ g++ 47-2.cpp -o 47-2
$ ./47-2
Exception Info: 
    ID: -3
    m_desc: Timeout Exception

3 标准库中的异常处理

标准库中的异常都是从 exception 类派生的,有两个重要分支

  • logic_error:常用于程序中的可避免逻辑错误
  • runtime_errror:常用于程序中无法避免的恶性错误

在这里插入图片描述

3.1 给Array数组类模板添加异常处理

【C++深度解析】45、数组类模板中我们使用数值型模板参数定义了数组类模板,在重载下标操作符 “[]” 时可能发生越界,这里我们添加异常处理,处理越界访问。使用标准库中的 out_of_range。

// Array.h
#ifndef _ARRAY_H_
#define _ARRAY_H_

#include<stdexcept>
using namespace std;

template<typename T, int N>
class Array
{
    T m_array[N];
public:
    int length() const;				// 改为const成员函数,const对象和非const对象都可以调用
    bool set(int index, T value);
    bool get(int index, T& value);
    T& operator[] (int index);
    T operator[] (int index) const;
    virtual ~Array();
};

template<typename T, int N>
int Array<T, N>::length() const
{
    return N;
}

template<typename T, int N>
bool Array<T, N>::set(int index, T value)
{
    bool ret = (index >= 0) && (index < N);
    if (ret)
    {
        m_array[index] = value;
    }
    return ret;
}

template<typename T, int N>
bool Array<T, N>::get(int index, T& value)
{
    bool ret = (index >= 0) && (index < N);
    if (ret)
    {
        value = m_array[index];
    }
    return ret;
}

template<typename T, int N>
T& Array<T, N>::operator[] (int index)
{
    if ( (index >= 0) && (index < N))
    {
        return m_array[index];
    }
    else					// 增加越界访问,抛出异常
    {
        throw out_of_range("T& Arrau<T, N>::operator[] (int index)");
    }
}

template<typename T, int N>
T Array<T, N>::operator[] (int index) const
{
    if ( (index >= 0) && (index < N) )
    {
        return m_array[index];
    }
    else					// 增加越界访问,抛出异常
    {
        throw out_of_range("T Array<T, N>::operator[] (int index)");
    }
}

template<typename T, int N>
Array<T, N>::~Array()
{

}
#endif
// 47-3.cpp
#include<iostream>
#include"Array.h"
using namespace std;
void TestArray()
{
    Array<int, 5> a;
    for (int i = 0; i < 10; i++)		// 这里故意越界访问
    {
        a[i] = i + 1;
    }
    for (int i = 0; i < a.length(); i++)
    {
        cout << a[i] << endl;
    }
}
int main()
{
    try
    {
        TestArray();
    }
    catch(...)
    {
        cout << "Exception" << endl;
    }
    return 0;
}
$ g++ 47-3.cpp -o 47-3
$ ./47-3
Exception

3.2 给HeapArray数组类模板添加异常处理

【C++深度解析】12、构造函数、拷贝构造函数和析构函数中我们定义了数组类,并增加拷贝构造函数实现深拷贝。为了防止生成半成品对象,在【C++深度解析】17、二阶构造模式中用二阶构造进行改进。在【C++深度解析】45、数组类模板中用模板加以改造。

重载下表操作符 “[]” 时我们没有处理越界异常,这里添加越界异常处理,用 out_of_range。

// HeapArray.h
#ifndef _HEAPARRAY_H_
#define _HEAPARRAY_H_
#include<stdexcept>
using namespace std;

template<typename T>
class HeapArray
{
    int m_length;
    T* m_pointer;
    HeapArray(int len);
    HeapArray(const HeapArray<T>& obj);
    bool construct();
public:
    static HeapArray<T>* NewInstance(int length);
    int length() const;					// 改为const成员函数,const对象和非const对象都可以调用
    bool get(int index, T& value);
    bool set(int index, T value);
    T& operator[] (int index);
    T operator[] (int index) const;
    HeapArray<T>& self();
    const HeapArray<T>& self() const;	// 增加一个const成员函数
    ~HeapArray();
};

template<typename T>
HeapArray<T>::HeapArray(int len)
{ 
    m_length = len;
}

template<typename T>
bool HeapArray<T>::construct()
{
    m_pointer = new T[m_length];
    return m_pointer != NULL;
}

template<typename T>
HeapArray<T>* HeapArray<T>::NewInstance(int length)
{
    HeapArray<T>* ret = new HeapArray<T>(length);
    if ( !(ret && ret->construct()) )
    {
        delete ret;
        ret = NULL;
    }
    return ret;
}

template<typename T>
int HeapArray<T>::length() const
{
    return m_length;
}

template<typename T>
bool HeapArray<T>::get(int index, T& value)
{
    bool ret = (index >= 0) && (index < m_length);
    if (ret)
    {
        value = m_pointer[index];
    }
    return ret;
}

template<typename T>
bool HeapArray<T>::set(int index, T value)
{
    bool ret = (index >= 0) && (index < m_length);
    if (ret)
    {
        m_pointer[index] = value;
    }
    return ret;
}

template<typename T>
T& HeapArray<T>::operator[] (int index)
{
    if ( (index >= 0) && (index < m_length))
    {
        return m_pointer[index];
    }
    else				// 增加越界访问异常处理
    {
        throw out_of_range("T& HeapArray<T>::operator[] (int index)");
    }
}

template<typename T>
T HeapArray<T>::operator[] (int index) const
{
    if ( (index >=0 ) && (index < m_length) )
    {
        return m_pointer[index];
    }
    else				// 增加越界访问异常处理
    {
        throw out_of_range("T HeapArray<T>::operator[] (int index) const");
    }
}

template<typename T>
HeapArray<T>& HeapArray<T>::self()
{
    return *this;
}

template<typename T>
const HeapArray<T>& HeapArray<T>::self() const
{
    return *this;
}

template<typename T>
HeapArray<T>::~HeapArray()
{
    delete[] m_pointer;
}

#endif
// 47-4.cpp
#include<iostream>
#include"HeapArray.h"
using namespace std;

void TestHeapArray()
{
    HeapArray<double>* pa = HeapArray<double>::NewInstance(5);
    if (pa)
    {
        HeapArray<double>& array = pa->self();
        for (int i = 0; i < 10; i++)			// 故意越界访问
    	{
        	array[i] = i;
    	}
    	for (int i = 0; i < array.length(); i++)
    	{
        	cout << array[i] << endl;
    	}
    }
    delete pa;
}

int main()
{
    try
    {
        TestHeapArray();
    }
    catch(...)
    {
        cout << "Exception" << endl;
    }
    return 0;
}
$ g++ 47-4.cpp -o 47-4
$ ./47-4
Exception

4 小结

1、catch 可以将捕获的异常抛出
2、异常类型可以是自定义的类类型
3、由于赋值兼容原则,先匹配子类异常,再匹配父类异常

发布了298 篇原创文章 · 获赞 181 · 访问量 10万+

猜你喜欢

转载自blog.csdn.net/happyjacob/article/details/104627343
今日推荐