//c++中设计自己的异常类 #include <iostream> using namespace std; const int DefaultSize = 10; //动态数组 class Array { public: Array(int itsSize = DefaultSize); ~Array() { delete[] pType; } //运算符重载 int& operator[](int offset); const int& operator[](int offset) const; //访问器:accessors int GeitsSize() const//能获得这个私有变量 { return itsSize; } //异常类 class XBoundary{}; class xSize{ public: xSize(){}; xSize(int size) :itsSize(size){} ~xSize(){} int GetSize(){ return itsSize; } virtual void PritError() { cout << "下标发生错误:" << itsSize << endl; } private: int itsSize; }; class xZero:public xSize{ public: xZero(int size) :xSize(size){} virtual void PritError() { cout << "下标不能是0:" << endl; } }; class xNegative :public xSize{ public: xNegative(int size) :xSize(size){} virtual void PritError() { cout << "下标不能是负数:" << xSize::GetSize() << endl; } }; class xTooSmall :public xSize{ public: xTooSmall(int size) :xSize(size){} virtual void PritError() { cout << "下标不能小于10:" << xSize::GetSize() <<endl; } }; class xToobig :public xSize{ public: xToobig(int size) :xSize(size){} virtual void PritError() { cout << "下标不能太大于3000:" << xSize::GetSize() << endl; } }; private: int *pType; int itsSize; }; //运算符重载 int& Array::operator [](int offset) { int size = this->GeitsSize(); if (offset >= 0 && offset < size) { return pType[offset]; } else{ throw XBoundary(); } } //常函数 用来读 const int& Array::operator [](int offset) const { int size = this->GeitsSize(); if (offset >= 0 && offset < size) { return pType[offset]; } else{ throw XBoundary(); } } Array::Array(int size) :itsSize(size) { if (size == 0) { throw xZero(size); }else if (size < 0){ throw xNegative(size); }else if (size > 3000){ throw xToobig(size); }else if (size < 10){ throw xTooSmall(size); } pType = new int[size]; for (int i = 0; i < size; i++) { pType[i] = 0; } } int main() { try { Array a; Array b(12); Array a(8); b[6] = 44; b[22] = 434; cout << b[6] << endl; cout << b[22] << endl; } catch (Array::XBoundary)//写在前面先捕获 { cout << "下标越界了" << endl; } catch (Array::xSize &exp)//利用基类指向子类 多态 { exp.PritError(); } //catch (Array::xZero theException) //{ // cout << "下标不能等于0:" << theException.GetSize() << endl; //} //catch (Array::xNegative theException) //{ // cout << "下标为负数:" << theException.GetSize() << endl; //} //catch (Array::xTooSmall theException) //{ // cout << "下标不能小于10:" << theException.GetSize() << endl; //} //catch (Array::xToobig theException) //{ // cout << "下标不能大于3000:" << theException.GetSize() << endl; //} //catch (...)//这句话如果放在最之前,就会把其他捕捉直接屏蔽掉,所以这个捕捉的顺序是有讲究的 //{ // cout << "发生未知异常!" <<endl; //} //Array intArray(20); //try //{ // for (int i = 0; i < 25; i++) // { // intArray[i] = i; // cout << "intArray[" << i << "] okay..." << endl; // } //} //catch (Array::XBoundary) //{ // cout << "下标越界了" << endl; //} system("pause"); return 0; }
其中运用到多态的概念
#include <iostream> #include <stdlib.h> using namespace std; /* 那么多态的作用是什么呢,封装可以使得代码模块化,继承可以扩展已存在的代码,他们的目的都是为了代码重用。 而多态的目的则是为了接口重用。 也就是说,不论传递过来的究竟是那个类的对象,函数都能够通过同一个接口调用到适应各自对象的实现方法。 最常见的用法就是声明基类的指针,利用该指针指向任意一个子类对象,调用相应的虚函数,可以根据指向的子类的不同而实现不同的方法。 或者可以利用基类的引用,指向任意一个子类对象 如果没有使用虚函数的话,即没有利用C++多态性,则利用基类指针调用相应的函数的时候,将总被限制在基类函数本身,而无法调用到子类中被重写过的函数。 */ class Father { public: void Face() { cout << "我是爸爸1" << endl; } virtual void say() { cout << "叫爸爸" << endl; } }; class Son:public Father { public: virtual void say() { cout << "叫儿子" << endl; } void Face() { cout << "我是儿子1" << endl; } }; void main() { Son son1; Father f1; Father *pFather = &son1;//隐式类型转换 基类指针指向子类对象 pFather->say(); Father &pFather1 = son1;//隐式类型转换 基类引用 pFather1.say(); Son *ptr = (Son *)&f1; ptr->Face(); //这是一个用子类的指针去指向一个强制转换为子类地址的基类对象 //从原理来说:由于ptr是子类指针,虽然被赋予了基类对象的地址, //在调用的时候,由于地址偏移量固定,偏移量是子类对象的偏移量, //于是即使在指向一个基类对象的情况下,依然调用的是子类的函数 ptr->say(); //可能还是因为C++多态性的原因,由于指向的是一个基类对象,通过虚函数列表的引用,找到了基类中say()函数的地址,因此调用了基类的函数。 system("pause"); }
C++库定义了很多基于exception的异常类型。
1,stdexcept 异常类
该文件定义了logic_error和runtime_error类,它们都是以公有类从exception派生而来的。
每个类所在的头文件在图下方标识出来.
标准异常类的成员:
① 在上述继承体系中,每个类都有提供了构造函数、复制构造函数、和赋值操作符重载。
② logic_error类及其子类、runtime_error类及其子类,它们的构造函数是接受一个string类型的形式参数,用于异常信息的描述;
③ 所有的异常类都有一个what()方法,返回const char* 类型(C风格字符串)的值,描述异常信息。
异常名称 |
描述 |
exception | 所有标准异常类的父类 |
bad_alloc | 当operator new and operator new[],请求分配内存失败时 |
bad_exception | 这是个特殊的异常,如果函数的异常抛出列表里声明了bad_exception异常,当函数内部抛出了异常抛出列表中没有的异常,这是调用的unexpected函数中若抛出异常,不论什么类型,都会被替换为bad_exception类型 |
bad_typeid | 使用typeid操作符,操作一个NULL指针,而该指针是带有虚函数的类,这时抛出bad_typeid异常 |
bad_cast | 使用dynamic_cast转换引用失败的时候 |
ios_base::failure | io操作过程出现错误 |
logic_error | 逻辑错误,可以在运行前检测的错误 |
runtime_error | 运行时错误,仅在运行时才可以检测的错误 |
logic_error的子类:
异常名称 |
描述 |
length_error | 试图生成一个超出该类型最大长度的对象时,例如vector的resize操作 |
domain_error | 参数的值域错误,主要用在数学函数中。例如使用一个负值调用只能操作非负数的函数 |
out_of_range | 超出有效范围 |
invalid_argument | 参数不合适。在标准库中,当利用string对象构造bitset时,而string中的字符不是’0’或’1’的时候,抛出该异常 |
runtime_error的子类:
异常名称 |
描述 |
range_error | 计算结果超出了有意义的值域范围 |
overflow_error | 算术计算上溢 |
underflow_error | 算术计算下溢 |
例:logic_error下的
domain_error:定义域由参数的可能取值组成
invalid_argument:值域由函数可能的返回值组成,指函数传递了一个意料之外的值
length_error:指没有足够的空间来执行所需的操作,如string类的append()方法合并得到的字符串长度超过最大允许长度会引发该异常
out_of_bounds:通常指索引错误,如定义一个数组,其operator[]在使用索引无效时会引发该异常
一般而言:
logic_error系列异常表明存在可以通过编程修复的问题。
runtime_error系列异常表明存在无法避免的问题。
2,对于bad_alloc 异常与new
对于使用new导致的内存分配问题,C++的最新处理方式是让new引发bad_alloc异常。
头文件new包含bad_alloc类的声明,它从exception公有派生来。
//标准异常类 #include <iostream> #include <new>//new 中包含这些标准异常类 using namespace std; class Dog { public: Dog() { parr = new int[1000000];//4MB } ~Dog(); private: int *parr; }; int main() { Dog *pDog; try{ for (int i = 0; i < 1000; i++)//4Gb { pDog = new Dog(); cout << i << ":new Dog成功" << endl; } } catch (bad_alloc err) { cout << "new Dog失败:" << err.what() << endl; } }
下面的可能会更让你理解
//标准异常类 #include <iostream> #include <new>//new 中包含这些标准异常类 #include <cstdlib>//用于EXIT_SUCCESSGN EXIT——FAILURE using namespace std; //EXIT_FAILURE 可以作为exit()的参数来使用,表示没有成功地执行一个程序 //EXIT_SUCCESS 作为exit()的参数来使用,表示成功地执行一个程序 struct Big { double stuff[20000]; }; int main() { Big* pb =nullptr; try { cout << "Trying to get a big block of memory:\n"; //pb = new Big[1000]; pb = new Big[1111000]; cout << "Memory successfully allocated\n"; pb[0].stuff[0] = 4; cout << pb[0].stuff[0] << endl; } catch (bad_alloc& ba) { cout << "Caught the exception!\n"; cout << ba.what() << endl; } delete[] pb; system("pause"); return 0; }
#include <iostream> #include <bitset> #include <string> #include <stdexcept> using namespace std; //logic_error的子类: 逻辑错误,可以在运行前检测的错误 //invalid_argument 参数不合适。在标准库中,当利用string对象构造bitset时,而string中的字符不是’0’或’1’的时候,抛出该异常 int main() { try{ string s{"101a110010"}; //C++的 bitset 在 bitset 头文件中,它是一种类似数组的结构,它的每一个元素只能是0或1,每个元素仅用1bit空间。 bitset<10> b(s); cout << "bitset ok" << endl; } catch (invalid_argument err) { cout << "bitset error:" << err.what() << endl; } system("pause"); return 0; }
#include <iostream> #include <stdexcept> //标准异常库所在 using namespace std; class 学生 { public: 学生(int 年龄) { if (年龄 < 0 || 年龄 > 150) { throw out_of_range("年龄不能小于0或大于100"); } this->m_年龄 = 年龄; } private: int m_年龄; }; //out_of_range 超出有效范围 //当我们捕获基类的引用时,对what函数的调用将执行与异常对象动态类型对应版本 int main() { try { 学生 张飞(220); cout << "学生年龄没错" << endl; } catch (out_of_range err) { cout << "学生年龄出错:" << err.what() << endl; } system("pause"); return 0; }
参考C++ 异常处理 exception类博客,了解异常类基础知识
下面是一个自己写的异常类包含标准异常类
//Stack.h头文件 #ifndef STACK_H #define STACK_H #include <exception> #include <deque>// template <class T> class Stack//先进后出 { protected: std::deque<T> c; public: class ReadEmptyStack :public std::exception//继承标准异常类 { public: virtual const char *what() const throw() { return "read empty stack堆栈是空的"; } }; bool empty() const { return c.empty(); } void push(const T &elem) { c.push_back(elem); } T pop() { if (c.empty()) { throw ReadEmptyStack(); } T elem(c.back());//把最后的元素拿出来 c.pop_back();//删除 return elem; } T& top()//读取栈顶元素 { if (c.empty()) { throw ReadEmptyStack(); } return c.back(); } }; #endif //mode_标准异常类2_自己做的异常类包含标准异常类 #include <iostream> #include "Stack.h" using namespace std; int main() { try{ Stack<int > st; st.push(1); st.push(2); st.push(3); cout << st.pop() << endl;//删除 cout << st.pop() << endl; cout << st.top() << endl;//只是查看栈顶元素 cout << st.pop() << endl; cout << st.pop() << endl; } catch (const exception &e) { cout << "发生异常:" << e.what() << endl; } cout << "hello Stack" << endl; system("pause"); return 0; }