目录
编程实验:创建异常类族
#ifndef EXCEPTION_H
#define EXCEPTION_H
#include "object.h"
//#define EXCEPTION_TEST
//#define EXCEPTION_TEST_PARENT_CLASS
#define EXCEPTION_TEST_CHILD_CLASS
namespace JQLib
{
//父类
#define THROW_EXCEPTION(e,m) (throw e(m, __FILE__, __LINE__))
class Exception : public Object
{
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;
}
};
//空指针异常类
//Index out of bounds exception
class IndexOutOfBoundsExcpt: public Exception{
public:
IndexOutOfBoundsExcpt():Exception(0){};
IndexOutOfBoundsExcpt(const char* message):Exception(message){};
IndexOutOfBoundsExcpt(const char* file, int line):Exception(file, line){};
IndexOutOfBoundsExcpt(const char* message, const char* file, int line):Exception(message, file, line){};
IndexOutOfBoundsExcpt(const IndexOutOfBoundsExcpt& ae):Exception(ae){};
IndexOutOfBoundsExcpt& operator = (const IndexOutOfBoundsExcpt& ae){ return (*this = ae);};
};
//越界异常类
//No enough memory exception
class NoEnoughMemoryExcpt: public Exception{
public:
NoEnoughMemoryExcpt():Exception(0){};
NoEnoughMemoryExcpt(const char* message):Exception(message){};
NoEnoughMemoryExcpt(const char* file, int line):Exception(file, line){};
NoEnoughMemoryExcpt(const char* message, const char* file, int line):Exception(message, file, line){};
NoEnoughMemoryExcpt(const NoEnoughMemoryExcpt& ae):Exception(ae){};
NoEnoughMemoryExcpt& operator = (const NoEnoughMemoryExcpt& ae){ return (*this = ae);};
};
//内存不足异常类
//Null point exception
class NullPointExcpt: public Exception{
public:
NullPointExcpt():Exception(0){};
NullPointExcpt(const char* message):Exception(message){};
NullPointExcpt(const char* file, int line):Exception(file, line){};
NullPointExcpt(const char* message, const char* file, int line):Exception(message, file, line){};
NullPointExcpt(const NullPointExcpt& ae):Exception(ae){};
NullPointExcpt& operator = (const NullPointExcpt& ae){ return (*this = ae);};
};
//参数错误异常类
//Invalid Parameter exception
class InvalidParameterExcpt: public Exception{
public:
InvalidParameterExcpt():Exception(0){};
InvalidParameterExcpt(const char* message):Exception(message){};
InvalidParameterExcpt(const char* file, int line):Exception(file, line){};
InvalidParameterExcpt(const char* message, const char* file, int line):Exception(message, file, line){};
InvalidParameterExcpt(const InvalidParameterExcpt& ae):Exception(ae){};
InvalidParameterExcpt& operator = (const InvalidParameterExcpt& ae){ return (*this = ae);};
};
}
#endif // EXCEPTION_H
Exception.cpp
#include "Exception.h"
#include <cstring> //字符串相关头文件
#include <cstdlib>
using namespace std;
namespace JQLib
{
void Exception::init(const char* message, const char* file, int line)
{
//直接这样复制不可取,message这个指针可能在栈上、堆空间、全局数据区
//这样没办法控制message的生命周期,所以这样是不安全的
//m_message = message;
//所以可以拷贝一份字符串出来,先包含头文件
//复制到堆空间,然后m_message指向堆空间
m_message = strdup(message);
if(file != NULL)
{
//将行号转换成字符串
//定义一个辅助的数组
char sl[16] = {0};
itoa(line, sl, 10);
//+2:一个用来存冒号,一个用来存结束符
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);
}
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 "SmartPointer.h"
#include "Exception.h"
using namespace std;
using namespace JQLib;
#ifdef EXCEPTION_TEST
int main()
{
#ifdef EXCEPTION_TEST_CHILD_CLASS
try
{
THROW_EXCEPTION(ArithmeticException, "test");
}
//子类异常放在上部
catch(const ArithmeticException& e)
{
cout<<"catch(const ArithmeticException& e)"<<endl;
cout<<e.message()<<endl;
cout<<e.location()<<endl;
}
//父类异常放在下部
catch(const Exception& e)
{
cout<<"catch(const Exception& e)"<<endl;
cout<<e.message()<<endl;
cout<<e.location()<<endl;
}
#endif
#ifdef EXCEPTION_TEST_PARENT_CLASS
//这里直接用父类进行测试可以将virtual ~Exception() = 0;
//改为virtual ~Exception();让编译器编译通过
try
{
THROW_EXCEPTION(Exception, "test");
}
catch(const Exception& e)
{
cout<<"catch(const Exception& e)"<<endl;
cout<<e.message()<<endl;
cout<<e.location()<<endl;
}
#endif
return 0;
}
#endif
设计原则
在可复用代码库设计时,尽量使用面向对象技术进行架构,尽量使用异常处理机制分离正常逻辑和异常逻辑
小结
- 现代C++库必然包含充要的异常类族
- 所有库中的数据结构类都依赖于异常机制
- 异常机制能够分离库中代码的正常逻辑和异常逻辑