欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

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

程序员文章站 2022-05-29 17:49:05
...

1 catch 将捕获的异常抛出

  • catch 中捕获的异常可以被重新解释后抛出
    【C++深度解析】47、C++ 中的异常处理(下)
    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:常用于程序中无法避免的恶性错误

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

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、由于赋值兼容原则,先匹配子类异常,再匹配父类异常

相关标签: C++深度解析