C++的异常处理机制(二)

本文介绍C++异常处理机制中的throw类对象注意事项,包括异常的层次结构、标准异常类使用等,并通过实例代码展示了如何定义和抛出异常。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

目录

前言

C++的异常处理机制(一)大概介绍了异常处理机制,本文主要是对异常throw类对象的一些注意事项进行记录。其实和throw普通数据类型是一样的,只是要主要throw的时候对象的生命周期而已。最后,在catch的时候,最好使用对象的引用,而不要使用指针(指针会出现错误,要使用new xx)。同时,附带了解一下标准异常程序库。

异常的层次结构

#include <iostream>
using namespace std;
// 异常的层次结构
class myArray
{
public:
    myArray(int len);
    ~myArray();
    int& operator[](int index);
    int getLen();
public:
    class eSize
    {
    public:
        eSize(int size)
        {
            m_size = size;
        }
        virtual void printErr()
        {
            cout << "eSize:" << m_size << " ";
        }
    protected:
        int m_size;

    };
    class eNegative : public eSize
    {
    public:
        eNegative(int size):eSize(size)
        {
            ;
        }
        virtual void printErr()
        {
            cout << "eNegative:" << m_size << " ";
        }
    };
    class eTooBig : public eSize
    {
    public:
        eTooBig(int size):eSize(size)
        {
            ;
        }
        virtual void printErr()
        {
            cout << "eTooBig:" << m_size << " ";
        }
    };
    class eZero : public eSize
    {
    public:
        eZero(int size):eSize(size)
        {
            ;
        }
        virtual void printErr()
        {
            cout << "eZero:" << m_size << " ";
        }
    };
    class eTooSmall : public eSize
    {
     public:
        eTooSmall(int size):eSize(size)
        {
            ;
        }
        virtual void printErr()
        {
            cout << "eTooSmall:" << m_size << " ";
        }
    };

protected:
private:
    int* m_space;
    int m_len;
};

// 构造函数没有返回值 所以异常处理是一种好的错误处理方法
myArray::myArray(int len)
{
    if(len < 0)
    {
        // eNegative是内部类
        throw eNegative(len);
    }
    else if(len == 0)
    {
        throw eZero(len);
    }
    else if(len > 1000)
    {
        throw eTooBig(len);
    }
    else if(len < 3)
    {
        throw eTooSmall(len);
    }
    m_len = len;
    m_space = new int[len];
}

myArray::~myArray()
{
    if(m_space != NULL)
    {
        delete [] m_space;
        m_len = 0;
    }
}

// 重载类的[]操作符,这样可以像使用普通数组一样访问类中的元素
int& myArray::operator[](int index)
{
    return m_space[index];
}

int myArray::getLen()
{
    return m_len;
}

int main()
{
    try{
        myArray a(-5);
        for(int i=0; i<a.getLen(); i++)
        {
            a[i] = i+1;
            cout << "a[i]=" << a[i] << endl;
        }
        // 注意:a是在try语句块内定义的,作用域也就在这一对{}中
    }
    catch(myArray::eSize &e) // 使用多态,eSize是基类 printErr()是虚函数
    {
        e.printErr();
    }
    #if 0
    // 注意:使用引用,不要使用指针或者普通对象
    catch(myArray::eNegative &e)
    {
        cout <<"eNegative 类型异常" << endl;
    }
    catch(myArray::eZero &e)
    {
        cout <<"eZero 类型异常" << endl;
    }
    catch(myArray::eTooBig &e)
    {
        cout <<"eTooBig 类型异常" << endl;
    }
    catch(myArray::eTooSmall &e)
    {
        cout <<"eTooSmall 类型异常" << endl;
    }
    #endif
    catch(...)
    {
        cout <<"未知 类型异常" << endl;
    }

    return 0;
}

在这个程序中,我们在一个类的内部定义了异常类(内部类)。总结如下:
1. 对于内部异常类,我们可以写一个抽象基类,然后对于不同的异常继承这个抽象基类,然后在catch语句中就可以使用多态来进行不同的操作了。
2. 对于catch语句捕捉内部异常类的时候,最好使用引用。 普通对象,如try(eNegative e)会产生匿名对象,当然使用也是没有问题的。
3. catch(eNegative* e)最好不要使用,因为try{} catch{}严格按照类型匹配,传过来的只是一个指针而已,也就是throw的时候只是一个类的指针,但是类分配的内存已经被释放掉,所以throw的指针是个野指针,解决办法是throw new eNegative;,但是要在catch语句中delete,所以很麻烦。

标准异常类

C++标准提供了一组标准异常类,这些类以基类Exception开始,标准程序库抛出的所有异常,都派生于该基类,这些类构成如图所示的异常类的派生继承关系。该基类提供一个成员函数what(),用于返回错误信息(返回类型为const char*)。在Exception类中,what()函数的声明如下:

virtual const char* what() const throw();

1
这个表列出了各个具体异常类及定义它们的头文件。runtime_errorlogic_error是一些具体的异常类的基类,它们分别代表两大类异常。logic_error表示那些可以在程序中被预先检测到的异常,也就是说如果小心编写程序,这类异常能够避免,而runtime_error则表示那些难以被预先检测的异常。
2
一些编程语言规定只能抛掷某个类的派生类(Java),C++虽然没有这项强制的要求,但仍然可以这样实践。例如,在程序中可以使得所有抛出的异常皆派生自exception(或者直接抛出标准程序库提供的异常类型,或者从标准程序库提供的异常类派生出新的类),这样会带来很大的方便。
博主的理解是,标准异常库可以简要理解为一系列继承自一个基类的类。当我们加入了相应的头文件。相当于C++的这一系列类是文件作用域,所以可以直接定义对象来抛出异常。
logic_errorruntime_error两个类及其派生类,都有一个接收const string&型参数的构造函数。在构造异常对象的时候需要将具体的错误信息传递给该函数,如果调用该对象的what函数,就可以得到构造时提供的错误信息。也就是说别的继承自exception的类不一定有这个函数?我在code block中exception类如下:

 class exception
  {
  public:
    exception() _GLIBCXX_USE_NOEXCEPT { }
    virtual ~exception() _GLIBCXX_USE_NOEXCEPT;

    /** Returns a C-style character string describing the general cause
     *  of the current error.  */
    virtual const char* what() const _GLIBCXX_USE_NOEXCEPT;
  };

说明,我们只要重载virtual const char* what() const _GLIBCXX_USE_NOEXCEPT;就可以实现多态了,所以不一定要继承“runtime_error
这样的才行。当然对于不同的环境可能不一样,我们需要的就是找到对应的类定义去做就行了。

代码

#include <iostream>

using namespace std;

class myExecption:public exception
{
public:
    myExecption(const char* p)
    {
        this->m_p = p;
    }

    // 注意重载的时候 const _GLIBCXX_USE_NOEXCEPT一定要加上
    virtual const char* what()const _GLIBCXX_USE_NOEXCEPT
    {
        cout << "MyException: 类型" << m_p << endl;
        return m_p;
    }
protected:
private:
    const char *m_p;
};

void testMyExecption()
{
    throw myExecption("函数异常");
}

int main()
{
    try{
        testMyExecption();
    }
    catch(exception &e){
        e.what();
    }
    catch(...)
    {
        cout << "未知异常" << endl;
    }
    return 0;
}

总结

以前总是对各种标准库有一种高大上的感觉,但是实际上原理是互通的。想想来说只是自己没有这部分的需求,如果有一天需要自己写这种层次的库,或许就不会有一种隔离感了!
附上《传智播客扫地僧c++基础和进阶课堂讲义.docx》下载地址

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值