//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;
}