C++ 第二十一天 在谈异常

/*
异常以类似于将实参传递给函数的方式抛出和捕获。异常可以是可传给非引用的形参的任意类型的对象,这意味这必须能够辅助该类类型的对象。
	不存在数组或函数类型的异常。如果抛出一个数组,被抛出的对象转换为指向数组首元素地址,类似抛出一个函数,函数被转化为指向该函数的指针。
	执行throw的时候,不会执行跟在truw后面的语句,而是将控制从throw转移到匹配的catch,该catch可以是同一函数中局部的catch,也可以在直接或间接调用发生异常的 函数的另一个函数中。控制从一个地方转移到另一个地方,它的含义:
		1.沿着调用链的函数提早推出。
		2.一般而言,在处理异常的时候,抛出异常的块中的局部存储不存在了。
	因为在处理异常的时候会释放局部存储,所以被抛出的对象就不能在局部存储,而是用throw表达式初始化一个称为异常对象(exception object)的特殊对象,异常对象油编译器管理,而且保证驻留在可能被激活的任意catch都可以访问的空间。这个对象由throw创建,并且初始化为被抛出的表达式的副本。异常对象将传给对应的catch,并且在完全处理了异常之后撤销。
	(注:异常对象通过复制被抛出表达式的结果创建,该结果必须是可以赋值的类型。)

	析构函数应该从不抛出异常。
	构造函数与与异常:如果在构造函数对象的时候发生异常,则该对象可能只是初始化一部分,一些成员已经初始化,一些还没油,即使对象的部分被构造了,也要保证将会适当地撤销已构造的成员。类似的,在初始化数组或其他容器的时候,也可能发生异常,也要保证会适当的撤销已初始化的元素。
	未捕获的异常程序,将调用terminate函数,强行终止程序。

	捕获所有的异常格式:
		try{}catch(...){}
	捕获所有异常的catch自居可以与任意类型的异常匹配。
	catch(...)经常与重新抛出表达式结合使用,catch完成可做的所有局部工作,然后重新抛出异常:
		try{}catch(...){
			//.....
			throw; //重新抛出异常,不用指定异常类型,就一个throw关键字。
		}
	
	构造函数要处理来自构造函数初始化式的异常,唯一的方法是将构造函数编写为函数测试块,函数测试块的格式:
		模板、类名::类名(构造函数形参列表) try:初始化列表{
			//...
		}catch(要捕获的异常类型){
			//...
		}

	关键字try出现在成员初始化列表之前,并且测试块的符合语句包围了构造函数的函数体。catch子句可以处理从成员初始化列表抛出的异常,也可以处理从构造函数体中抛出的异常。

	exception类型所定义的唯一操作是一个名为what的虚成员,该函数返回const *char对象,它一般返回用来在抛出位置构造异常对象的信息。因为what是虚函数,如果捕获了基类类型引用,对what函数的调用将执行适合异常对象的动态类型的版本。
	
	可能存在异常的程序以及分配资源的程序应该使用类来管理那些资源,使用类管理分配和回收可以保证如果发生异常就释放资源。


auto_ptr类:auto_ptr类是接受一个类型形参的模板,它为动态分配的对象提供异常安全,auto类在头文件memory中定义。
	auto_ptr类的成员:
	auto_ptr<T> ap;		//创建名为ap的未绑定的auto_ptr对象
	auto_ptr<T> ap(p);	//创建名为ap的auto_ptr对象,ap拥有指针p指向的对象,该构造函数为explicit
	auto_ptr<T> ap1(ap2);	//创建名为ap1的auto_ptr对象,ap1保存原来存储在ap2中的指针,将所有权转给ap1,ap2成为未绑定的autp_ptr对象。
	ap1=ap2;		//将所有权从ap2转给ap1,删除ap1指向的对象并且使ap1指向ap指向的对象,使ap2成为未绑定的
	~ap			//析构函数,删除ap绑定对象的引用。
	*ap			//返回对ap所绑定的对象的引用。
	ap->			//返回ap保存的指针。
	ap.reset(p);		//如果p与ap的值不同,则删除ap指向的对象,并且将ap绑定到p
	ap.release();		//返回ap所保存的指针并且使ap成为未绑定的。
	ap.get();		//返回ap保存的指针。

(注意:auto_ptr只能用于管理从new返回的一个对象,它不能管理动态分配的数组,当autp_ptr被复制或赋值的时候,有不寻常的行为,因此,不能旧爱那个auto_ptr存储在标准库容器类型中。autp_ptr对象只能报ucnyige指向对象的指针,并且不能用于指定动态分配的数组,使用autp_ptr对象指向动态分配的数组,会导致未定义的运行时行为。当autp_ptr对象超出作用域或者另外撤销的时候,就自动回收autp_ptr所指向的动态分配对象。)

	因为auto_ptr的赋值和复制操作是破坏性的,所以不能将auto_ptr对象存储在标准容器中。标准的容器类要求在赋值或赋值之后两个对象相等,auto_ptr不满足这一要求,如果ap2赋值给ap1,则在赋之后ap1!=ap2,复制也类似。	

auto_ptr的缺陷:
	auto_ptr类模板为处理动态分配的内存提供了安全行和遍历性的尺度。要正确使用,必须强加下列限制:
	1.不要使用auto_ptr对象保存指向静态分配对象的指针,否则,当auto_ptr对象本身被撤销的时候,它将试图删除指向非动态分配对象的指针,导致未定义行为。
	2.永远不要使用两个auto_ptr对象指向同一个对象,导致这个错误的一种明显方式是,使用同一指针来初始化或者rest两个不同的auto_ptr对象,另一种是使用auto_ptr对象的get函数的将诶过来初始化或者reset另一个auto_ptr对象。
	3.不要使用auto_ptr对象保存指向动态分配数组的指针,当auto_ptr对象被删除的时候,它只释放一个对象----它使用普通的delete操作符,而不是数组的delete[] 操作符。
	4.不要将auto_ptr对象存储在容器中,容器要求所保存的类型定义赋值和复制操作符,使它们表现得类似于内置类型的操作符:在赋值或复制之后,两个对象必须具有相同值,auto_ptr类不满足这个要求。 

	

异常说明:
	1.异常说明跟在函数形参表之后,一个异常说明在关键字throw之后跟着一个由元括号扩住的异常类型列表,例如:
		void recoup(int) throw (runtime_error);
	该recoup抛出的异常将会是runtime_error对象,或者runtime_error派生的类型的异常。异常类型列表也可以为空:
		void no_problem() throw();
	空的说明列表指出函数不抛出任何异常。
	如果一个函数声明没有指定异常说明,则该函数可以抛出任意类型的异常。

	2.违反异常说明:
		如果函数抛出了没有在其异常说明中列出的异常,就调用标准库函数unexpected。默认情况下,unexpected函数调用terminate函数,terminate一般会终止程序。
	
	3.异常说明与虚函数:
		基类中虚函数的异常说明,可以与派生类中对应虚函数的异常说明不同,但是派生类虚函数的异常说明必须与对应基类虚函数的异常说明同样严格,或者更严格。这个限制保证,当使用基类类型的指针调用派生类虚函数的时候,派生类的异常说明不会增加新的可抛出异常。
	
	4.函数指针说明:
		void (*fntName) (形参) throw (异常形参)
	如果不提供明异常说明,则该指针就可以指向能够抛出任意类型异常的具有匹配类型的函数
*/	

#include<iostream>
#include<memory>
//#include<exception>
//包含异常的头文件
#include<stdexcept>
//
using namespace std;
//异常说明和虚函数:
class Base{
public:
	virtual double f1(double) throw();
	virtual int f2(int) throw (std::logic_error);
	virtual string f3() throw (std::logic_error,std::runtime_error);

};

class Derived:public Base{
public :
	//放宽异常限制,错误
	//double f1(double) throw(std::underflow_error);
	//抛出跟基类一行的异常说明
	int f2(int) throw(std::logic_error);
	//声明比基类虚函数更严格的异常说明
	std::string f3() throw();
};



int main(){
	//使用zuto_ptr对象,测试
	auto_ptr<string> ap(new string("best man"));
	//auto_ptr对象的赋值和赋值是破坏性操作:
	auto_ptr<string> ap1=ap;
	//测试auto_ptr对象,使用get操作
	//不能用get作为创建其他auto_ptr对象的实参:
	//使用get成员初始化其他autp_ptr对象违反auto_ptr类设计原则,在任意时刻只有一个auto_ptr对象保存给定指针,如果两个auto_ptr对象保存相同的指针,该指针就会被delete两次。
	if(ap.get()){
		cout<<"ap是有绑定指针的"<<endl;	
	}
	if(ap1.get())
		cout<<"ap1是有绑定指针的"<<endl;	
	//auto_ptr对象不能直接将一个地址赋值给auto_ptr对象,必须调用reset操作,该操作会删除原来保存的对象
	ap1.reset(new string("today"));
	
	cout<<*ap1<<endl;
//	cout<<*ap<<endl;
	return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值