目录
前言
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();
这个表列出了各个具体异常类及定义它们的头文件。runtime_error
和logic_error
是一些具体的异常类的基类,它们分别代表两大类异常。logic_error
表示那些可以在程序中被预先检测到的异常,也就是说如果小心编写程序,这类异常能够避免,而runtime_error
则表示那些难以被预先检测的异常。
一些编程语言规定只能抛掷某个类的派生类(Java),C++
虽然没有这项强制的要求,但仍然可以这样实践。例如,在程序中可以使得所有抛出的异常皆派生自exception
(或者直接抛出标准程序库提供的异常类型,或者从标准程序库提供的异常类派生出新的类),这样会带来很大的方便。
博主的理解是,标准异常库可以简要理解为一系列继承自一个基类的类。当我们加入了相应的头文件。相当于C++
的这一系列类是文件作用域,所以可以直接定义对象来抛出异常。
logic_error
和runtime_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》下载地址。