1. 异常类构建
(1)异常的类型可以是自定义类类型
(2)对于类类型异常的匹配依旧是至上而下严格匹配
(3)赋值兼容性原则在异常匹配中依然适用
(4)一般而言,匹配子类异常的catch放在上部,匹配父类异常的catch放在下部。
(5)现代C++库必然包含充要的异常类族
2. DTLib异常类功能定义和类图
(1)类图结构
(2)功能定义
异常类 |
功能描述 |
ArithmeticException |
计算异常 |
NullPointerException |
空指针异常 |
IndexOutOfBoundsException |
越界异常 |
NotEnoughMemoryException |
内存不足异常 |
InvalidParameterException |
参数错误异常 |
(3)设计原则:在可复用代码库设计时,尽量使用面向对象技术进行架构,尽量使用异常处理机分离正常逻辑和异常逻辑。
(4)异常类中的接口定义
class Exception { 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() = 0; };
【编程实验】创建异常类族
//Exception.h
#ifndef _EXCEPTION_H_ #define _EXCEPTION_H_ namespace DTLib { #define THROW_EXCEPTION(e, m) (throw e(m, __FILE__, __LINE__)) class Exception { protected: 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; //注意: //(1)析构函数是较为特殊的函数,一旦定义了析构函数,不管这个函数是不是纯虚函数,就 //必须提供实现。因为,对象在销毁时,最后都会调用父类的析构函数。如果父类不提供实现, //当对象销毁过程中调用到父类析构函数时,就找不到析构函数,也就不知该如何析构下去。 //因此,尽管这里将析构函数声明为纯虚函数,但Exception类仍提供析构函数的实现。以便 //最后正确释放掉m_message和m_location所指的堆空间. //(2)此外,声明为纯虚函数,可以让该类只能作为接口使用,而且也强迫子类必须 //提供析构函数的实现。 virtual ~Exception() = 0; //纯虚函数 }; //计算异常类 class ArithmeticException: public Exception { public: ArithmeticException():Exception(0){} ArithmeticException(const char* message):Exception(message){} ArithmeticException(const char*file, int line):Exception(file, line){} ArithmeticException(const char *message, const char* file, int line):Exception(message, file, line){} ArithmeticException(const ArithmeticException& e): Exception(e){} ArithmeticException& operator=(const ArithmeticException& e) { Exception::operator =(e); return *this; } }; //空指针异常类 class NullPointerException: public Exception { public: NullPointerException():Exception(0){} NullPointerException(const char* message):Exception(message){} NullPointerException(const char*file, int line):Exception(file, line){} NullPointerException(const char *message, const char* file, int line):Exception(message, file, line){} NullPointerException(const NullPointerException& e): Exception(e){} NullPointerException& operator=(const NullPointerException& e) { Exception::operator =(e); return *this; } }; //越界异常类 class IndexOutOfBoundsException: public Exception { public: IndexOutOfBoundsException():Exception(0){} IndexOutOfBoundsException(const char* message):Exception(message){} IndexOutOfBoundsException(const char*file, int line):Exception(file, line){} IndexOutOfBoundsException(const char *message, const char* file, int line):Exception(message, file, line){} IndexOutOfBoundsException(const IndexOutOfBoundsException& e): Exception(e){} IndexOutOfBoundsException& operator=(const IndexOutOfBoundsException& e) { Exception::operator =(e); return *this; } }; //内存不足异常类 class NotEnoughMemoryException: public Exception { public: NotEnoughMemoryException():Exception(0){} NotEnoughMemoryException(const char* message):Exception(message){} NotEnoughMemoryException(const char*file, int line):Exception(file, line){} NotEnoughMemoryException(const char *message, const char* file, int line):Exception(message, file, line){} NotEnoughMemoryException(const NotEnoughMemoryException& e): Exception(e){} NotEnoughMemoryException& operator=(const NotEnoughMemoryException& e) { Exception::operator =(e); return *this; } }; //参数错误异常类 class InvalidParameterException: public Exception { public: InvalidParameterException():Exception(0){} InvalidParameterException(const char* message):Exception(message){} InvalidParameterException(const char*file, int line):Exception(file, line){} InvalidParameterException(const char *message, const char* file, int line):Exception(message, file, line){} InvalidParameterException(const InvalidParameterException& e): Exception(e){} InvalidParameterException& operator=(const InvalidParameterException& e) { Exception::operator =(e); return *this; } }; } #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); //复制message的内容 if(file != NULL){ char sl[16]={0}; itoa(line, sl, 10);//将整数line转为字符串,其中的10表示转换为十进制格式 //m_location的格式为:file:line\0; m_location = static_cast<char*>(malloc(strlen(file) + strlen(sl) + 2)); m_location = strcpy(m_location, file); m_location = strcat(m_location, ":"); m_location = strcat(m_location, sl); } } //构造函数 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(ArithmeticException, "test"); }catch(const ArithmeticException& e){ //子类放catch前面 cout << "catch(const ArithmeticException& e)" << endl; cout << e.message() << endl; cout << e.location() << endl; }catch(const Exception& e){ //父类放catch后面 cout << "catch(const Exception& e)" << endl; cout << e.message() << endl; cout << e.location() << endl; } return 0; }
3. 小结
(1)现代C++库必然包含充要的异常类族
(2)所有库中的数据结构都依赖于异常机制
(3)异常机制能够分离库中代码的正常逻辑和异常逻辑。