c++中异常处理

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



猜你喜欢

转载自blog.csdn.net/weixin_40807247/article/details/80283482