65-C++中的异常处理(下)

注:博客中内容主要来自《狄泰软件学院》,博客仅当私人笔记使用。

测试环境:Ubuntu 10.10

GCC版本:9.2.0

 

一、C++中的异常处理

1)catch语句块中可以抛出异常

 需要其它处理异常的代码,接受异常。

try...catch...异常处理中,也有初始化,不过没有转换,必须严格匹配。

try...catch(...)...异常匹配为任意类型,但是不知道参数类型,只能throw再抛出异常(原有类型)

 

问题

            为什么要在catch中重新抛出异常?

2)catch中捕获的异常可以被重新解释后抛出

3)工程开发中使用这样的方式统一异常类型

 

编程实验
异常的重新解释
65-1.cpp
#include <iostream>
#include <string>

using namespace std;

void Demo()        //正常
{
    try
    {
        try
        {
            throw 'c';
        }
        catch(int i)
        {
            cout << "Inner: catch(int i)" << endl;
            throw i;
        }
        catch(...)    //第一次捕捉
        {
            cout << "Inner: catch(...)" << endl;
            throw;
        }
    }
    catch(...)        //外层,第二次捕捉
    {
        cout << "Outer: catch(...)" << endl;
    }
}


/*
    假设: 当前的函数是第三方库中的函数,因此,我们无法修改源代码
    
    函数名: 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(int argc, char *argv[])
{
    // Demo();
    
    try
    {
        MyFunc(11);
    }
    catch(const char* cs)    //字符串异常,参数类型必须为const char*
    {
        cout << "Exception Info: " << cs << endl;
    }
    
    return 0;
}

操作:

1) g++ 65-1.cpp -o 65-1.out编译正确,打印结果:

Inner:catch(...)
Outer:catch(...)

分析:

        证明异常可以被捕获进行重新解释。

 

2)在func中抛出异常:

#include <iostream>
#include <string>

using namespace std;

/*
    假设: 当前的函数是第三方库中的函数,因此,我们无法修改源代码
    
    函数名: 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(int argc, char *argv[])
{  
    try
    {
        MyFunc(11);
    }
    catch(const char* cs)    //字符串异常,参数类型必须为const char*
    {
        cout << "Exception Info: " << cs << endl;
    }
    
    return 0;
}

操作:g++ 65-1.cpp -o 65-1.out编译正确,打印结果:

Exception Info: Timeout Exception

分析:

        模拟使用第三方库中的函数,重新解释抛出的异常。

 

4)异常的类型可以是自定义类类型

5)对于类类型异常的匹配依旧是至上而下严格匹配

6)赋值兼容性原则在异常匹配中依然使用

7)一般而言

        -    匹配子类异常的catch放在上部

        -    匹配父类异常的catch放在下部

8)在工程中会定义一系列的异常类

9)每个类代表工程中可能出现的一种异常类型

10)代码复用时可能需要重解释不同的异常类

11)在定义catch语句块时推荐使用引用作为参数

编程实验
类类型的异常(使用展示)
65-2.cpp
#include <iostream>
#include <string>

using namespace std;

class Base
{
};

class Exception : public Base
{
    int m_id;            //异常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;
    }
};


/*
    假设: 当前的函数式第三方库中的函数,因此,我们无法修改源代码
    
    函数名: 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(int argc, char *argv[])
{
    try
    {
        MyFunc(11);
    }
    catch(const Exception& e)    //防止拷贝构造,提高程序效率
    {
        cout << "Exception Info: " << endl;
        cout << "   ID: " << e.id() << endl;
        cout << "   Description: " << e.description() << endl;
    }
    catch(const Base& e)
    {
        cout << "catch(const Base& e)" << endl;
    }
    
    return 0;
}

操作:

1) g++ 65-2.cpp -o 65-2.out编译正确,打印结果:

Exception Info:
ID: -3
Description: Timeout Exception    

将父类异常处理放在子类前边:

int main(int argc, char *argv[])
{
    try
    {
        MyFunc(11);
    }
    catch(const Base& e)//如果将父类放在前边,父子兼容性原则,就去执行父类异常
    {
        cout << "catch(const Base& e)" << endl;
    }
    catch(const Exception& e)    //防止拷贝构造,提高程序效率
    {
        cout << "Exception Info: " << endl;
        cout << "   ID: " << e.id() << endl;
        cout << "   Description: " << e.description() << endl;
    }    
    
    return 0;
}

g++ 65-2.cpp -o 65-2.out编译有警告:

65-2.cpp:86:2: warning: exception of type ‘Exception’ will be caught [enabled by default]
  catch(const Exception& e)
  ^
警告:'Exception'异常类型将要被捕获(子类能被父类类型捕获)  
65-2.cpp:82:2: warning:    by earlier handler for ‘Base’ [enabled by default]
  catch(const Base& e)
警告:最先被'Base'处理

分析:

        证明:捕获异常时由于赋值兼容性原则,父类类型可以捕获子类类型异常。因此父类类型建议放在子类异常处理后边。

 

12)C++标准库中提供了实用异常类族

13)标准库中的异常都是从exception类派生的

14)exception类有两个主要的分支

        -    logic_error

            *常用于程序中的可避免逻辑错误

        -    runtime_error

            *常用于程序中无法避免的恶性错误

15)标准库中的异常

 

编程实验
标准库中的异常使用
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;
    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 = (0 <= index) && (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 = (0 <= index) && (index < N);
    
    if( ret )
    {
        value = m_array[index];
    }
    
    return ret;
}

template
< typename T, int N >
T& Array<T, N>::operator[] (int index)
{
    if( (0 <= index) && (index < N) )
    {
        return m_array[index];
    }
    else
    {
        throw out_of_range("T& Array<T, N>::operator[] (int index)");
    }
}

template
< typename T, int N >
T Array<T, N>::operator[] (int index) const
{
    if( (0 <= index) && (index < N) )
    {
        return m_array[index];
    }
    else
    {
        throw out_of_range("T Array<T, N>::operator[] (int index) const");
    }
}

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

}

#endif


heapArray.h
#ifndef _HEAPARRAY_H_
#define _HEAPARRAY_H_

#include <stdexcept>

using namespace std;

template
< typename T >
class HeapArray
{
private:
    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;
    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;
    ~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 = 0;
    }
        
    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 = (0 <= index) && (index < length());
    
    if( ret )
    {
        value = m_pointer[index];
    }
    
    return ret;
}

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

template
< typename T >
T& HeapArray<T>::operator [] (int index)
{
    if( (0 <= index) && (index < 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( (0 <= index) && (index < 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

65-3.cpp
#include <iostream>
#include <string>
#include "Array.h"
#include "HeapArray.h"

using namespace std;

void TestArray()
{
    Array<int, 5> a;
    
    for(int i=0; i<a.length(); i++)
    {
        a[i] = i;
    }
        
    for(int i=0; i<a.length(); i++)
    {
        cout << a[i] << endl;
    }
}

void TestHeapArray()
{
    HeapArray<double>* pa = HeapArray<double>::NewInstance(5);
    
    if( pa != NULL )
    {
        HeapArray<double>& array = pa->self();
        
        for(int i=0; i<array.length(); i++)
        {
            array[i] = i;
        }
            
        for(int i=0; i<array.length(); i++)
        {
            cout << array[i] << endl;
        }
    }
    
    delete pa;
}

int main(int argc, char *argv[])
{
    
    try
    {
        TestArray();
        
        cout << endl;
        
        TestHeapArray();
    }
    catch(...)
    {
        cout << "Exception" << endl;
    }
    
    return 0;
}

 

小结

1)catch语句块中可以抛出异常

2)异常的类型可以是自定义类类型

3)赋值兼容性原则在异常匹配中依然使用

4)标准库中的异常都是从exception类派生的

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值