我们在之前学习了 C++ 中有关异常的知识,现在我们来重新回顾下。那么异常的格式是什么呢?便是 try ... catch ...try 语句处理正常的代码逻辑,而 catch 语句则处理异常情况,try 语句中的异常由对应的 catch 语句处理。格式如下

try
{
    double r = divide(1, 0);
}
catch(...)
{
    cout << "Divided by zero ..." << endl;
}

        在 C++ 中,通过 throw 语句抛出异常信息。throw 抛出的异常必须被 catch 处理,当前函数如果能处理异常,程序将继续往下执行;如果当前函数无法处理异常则函数停止执行并返回。未被处理的异常会顺着函数调用栈向上传播,直到被处理为止,否则程序将停止执行。如下

图片.png

        同一个 try 语句可以跟上多个 catch 语句。catch 语句可以定义具体处理的异常类型,不同类型的异常由不同的 catch 语句负责处理;try 语句中可以抛出任何类型的异常,catch(...) 用于处理所有类型的异常,任何异常都只能被捕获(catch)一次。异常处理的匹配规则如下

图片.png

        异常的类型可以是自定义类类型,对于类类型异常的匹配依旧是至上而下严格匹配,赋值兼容性原则在异常匹配中依然适用。一般而言:匹配子类异常的 catch 放在上部,匹配父类异常的 catch 放在下部。现代 C++ 库中必然包含充要的异常类族,因为异常类是数据结构类所依赖的“基础设施”!举个例子,如果我们在申请内存失败时便要但会一个内存不足的异常。异常类如下图所示

图片.png

        下来我们来看看异常类功能定义,如下

图片.png

        下来我们来创建一个异常类族,代码如下


Exception.h 源文件

#ifndef EXCEPTION_H#define EXCEPTION_H

#include "Object.h"

namespace DTLib
{

class Exception : public Object
{
private:
    char* m_message;
    char* m_location;

    void init(const char* message, const char* file, int line);
public:
    Exception(const char* message);
    Exception(const char* file, int line);
    Exception(const char* message, const char* file, int line);

    Exception(const Exception& e);
    Exception& operator= (const Exception& e);

    virtual const char* message() const;
    virtual const char* location() const;

    virtual ~Exception();
};

#endif // EXCEPTION_H

      

Exception.cpp 源码如下

#include "Exception.h"#include <cstring>
#include <cstdlib>

using namespace std;

namespace DTLib
{

void Exception::init(const char* message, const char* file, int line)
{
    m_message = strdup(message);

    if( file != NULL )
    {
        char s1[16] = {0};

        itoa(line, s1, 10);

        m_location = static_cast<char*>(malloc(strlen(file) + strlen(s1) + 2));
        m_location = strcpy(m_location, file);
        m_location = strcat(m_location, ":");
        m_location = strcat(m_location, s1);
    }
    else
    {
        m_location = NULL;
    }
}

Exception::Exception(const char* message)
{
    init(message, NULL, 0);
}

Exception::Exception(const char* file, int line)
{
    init(NULL, file, line);
}

Exception::Exception(const char* message, const char* file, int line)
{
    init(message, file, line);
}

Exception::Exception(const Exception& e)
{
    m_message = strdup(e.m_message);
    m_location = strdup(e.m_location);
}

Exception& Exception::operator= (const Exception& e)
{
    if( this != &e )
    {
        free(m_message);
        free(m_location);

        m_message = strdup(e.m_message);
        m_location = strdup(e.m_location);
    }

    return *this;
}

const char* Exception::message() const
{
    return m_message;
}

const char* Exception::location() const
{
    return m_location;
}

Exception::~Exception()
{
    free(m_message);
    free(m_location);
}

}

 

main.cpp 源文件如下

#include <iostream>#include "Exception.h"

using namespace std;
using namespace DTLib;

int main()
{
    try
    {
        throw Exception("test", __FILE__, __LINE__);
    }
    catch(const Exception& e)
    {
        cout << "catch(const Exception& e)" << endl;
        cout << e.message() << endl;
        cout << e.location() << endl;
    }

    return 0;
}

        下来我们来看看编译结果       

图片.png

        我们看到抛出了一个异常,它的文件名是 test,行号是 11 行。我们可以在 Exception.h 头文件添加一个宏,用来显示文件名及行号,定义如下

#define THROW_EXCEPTION(e, m) (throw e(m, __FILE__, __LINE__))

        然后将 mian.cpp 中的 throw 语句中的抛异常改为下面这样

THROW_EXCEPTION(Exception, "test");

        编译结果如下

图片.png

        我们看到效果是一样的。那么在可复用代码库设计时,尽量使用卖你想对象技术进行架构,尽量使用异常处理机制分类正常逻辑和异常逻辑。通过今天对异常类的学习,总结如下:1、C++ 中国直接支持异常处理的概念,try ... catch ... 是 C++ 中异常处理的专用语句;2、try 语句处理的是正常代码逻辑,catch 语句处理的是异常情况;3、同一个 try 语句可以跟上多个 catch 语句,异常处理必须严格匹配,不进行任何的类型转换;4、现代 C++ 库必然包含充要的异常类族,所有库中的数据结构都依赖于异常机制;5、异常机制能够分离库中代码的正常逻辑个异常逻辑。