EssentialC++第七章总结+课后习题+踩雷清单(最后一期,至此EssentialC++完结!)

本文深入探讨C++中的异常处理机制,包括抛出与捕获异常、局部资源管理及标准异常类的使用。同时,通过实例展示了如何在函数中有效处理异常,确保程序的健壮性和安全性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

总结

这一章讲了异常处理。

  • 首先你得有个抛出异常,用throw表达式,可以throw整数、字符串、异常类类对象,可以定义异常类
  • 然后需要捕获异常,用catch子句,catch{//…}异常对象的类型是会和捕获并处理异常的catch子句比对的,看哪个catch子句能处理这种类型的异常对象,catch子句可以重新抛出异常交由其他catch子句处理,就在catch子句最后写个throw;就行。
  • 捕获任何类型异常,写catch(…){//…}
  • 然后讲了提炼异常,介绍try块,try{//…}和catch子句搭配,catch子句紧接try{}之后,当try块内的程序代码执行时,如果try块有任何异常抛出,捕获catch()内指定的异常。
  • 从提炼异常引申到了异常处理机制,如果异常不能在某个地方被处理(某个函数里),就会终止这个函数,向调用这个函数的地方寻求可处理异常的catch子句(可能会不断解开“函数调用链”)
  • 如果main()函数还找不到能处理异常的catch子句,则调用C++标准库的terminate()函数来终结main()函数(整个程序)的运行。
  • 如果异常能在某个地方被处理(比如某个函数里),则该地方(函数)的剩余代码可继续被执行
  • 接下来是局部资源管理,C++保证函数中的所有局部对象的析构函数都会被调用。
  • 介绍了auto_ptr,使用前包含memory头文件,auto_ptr将*和->运算符重载,以便我们可以像用一般指针一样用auto_ptr对象。
  • 最后是介绍了标准异常。首先说明new表达式的具体工作步骤。然后介绍bad_alloc异常类。接着是异常类体系exception类,bad_alloc派生类派生自exception基类。
  • 自己编写的异常类也可以继承于exception基类之下,好处是可以让捕获抽象基类exception的程序代码(catch子句)所捕获。使用/提到exception基类的程序代码必须包含头文件exception
  • 抛出自己编写的异常,打印自编的异常类里的what()函数(继承自exception基类)里输出的内容。
  • 最后介绍了ostringstream类和istringstream类,使用/提到这些类的程序代码必须包含头文件sstream
  • ostringstream类内提供内存内的输出操作,可以把不同类型的数据格式化为字符串并输出到一个string对象上(非字符型/string型数据格式化为string型),而istringstream类相反,作用是将表示非字符数据的字符串(string型)转换为其实际的类型,
  • ostringstream类和istringstream类作用都体现在其类对象<<" “(或非” "对象上)
  • ostringstream类有一个str()成员函数,用来返回转换成字符串(string型)的对象
  • string类有一个成员函数c_str(),把string型字符串转换为const char*型,并返回const char*型字符串。

习题

7.1

  • ifstream类的构造函数形参类型是const char*,如果有以下操作:
int *alloc_and_init(string file_name)
{
	ifstream infile(file_name);
	if(!infile)
	{
		//处理打开文件失败的操作(处理异常)
	}
	
	//...
}

是不可以的。ifstream infile(file_name);要改为ifstream infile(file_name.c_str());
如果有以下操作:

int elem_cnt;
infile>>elem_cnt;

一定在读取文件的内容进elem_cnt后检查一下读取的到底是个什么东西,如果文件是个空文件或者字符/字符串文件,infile>>elem_cnt;的操作就会失败,就要在这行代码下加if(!infile){//...}来处理如果读取空文件或者读取的是非数值内容(这种情况下会读取失败,infile=0)的这种异常操作。如果读取失败,infile会表现出infile前一个操作执行后的状态(infile=0)

一定注意指针的指向是不是实际的对象而非0或者不知道指向了哪。比如以下操作:

int *pi=allocate_array(elem_cnt);
if(!pi)
{
	//allocate_array()没有分配到足够内存,也就没法让指针指向拟分配的内存空间
}

如果allocate_array()函数没有分配到足够内存,pi指针就会被设置为0(空指针),所以在进行pi指针操作前一定要写if(!pi){//…}来检查pi是不是0

7.2

下列函数被上题的alloc_and_ini()调用,执行失败时会发出异常:

allocate_array()//发出异常noMem
sort_array()//发出异常int
register_data()//发出异常string

写一个/多个try块,以及相应的catch子句,来处理这些异常,相应的catch字句只需要打印错误信息。
用一个try块里给三个相应的catch子句来处理三个相应的异常

int *alloc_and_init(string file_name)
{
	ifstream infile(file_name.c_str());
	if(!infile)
	{
		return 0;
	}
	int elem_cnt;
	infile>>elem_cnt;
	if(!infile)
	{
		return 0;
	}
	try
	{
		int *pi=allocate_array(elem_cnt);
		int elem;
		int index=0;
		while(infile>>elem&&index<elem_cnt)
		{
			pi[index++]=elem;
		}
		sort_array(pi,elem_cnt);
		register_data(pi);
	}
	catch(const noMem &menFail)//noMem是一个类
	{
		cerr<<"错误信息";
		return 0;//终止*alloc_and_init函数运行
	}
	catch(const &sortFail)
	{
		cerr<<"错误信息";
		return 0;
	}
	catch(string &registerFail)
	{
		cerr<<"错误信息";
		return 0;
	}
	return pi;//没有任何异常被抛出,程序执行于此(意味着如果有抛出异常的情况
	//相应的catch子句会接收异常并处理,然后终止该函数的运行
}

学习一下这种处理方式!

7.3

在Stack类体系里加入两个异常类型,处理”空栈弹出元素“和”满栈压入元素“这两种异常情况。

  • 因为会定义处理这两种情况的异常类,供pop()和push()这两个Stack类的成员函数抛出异常,这样这两个成员函数就不用返回代表成功或失败的值(也就是不用再写if(栈空/栈满){cerr<<.....;}如此的操作了,pop()和push()这两个Stack类的成员函数可以从bool返回类型改为void返回类型了
    这就是有异常类的好处

  • 为了让这两个Stack异常可以被完全不知情的其他组件捕获,他们应该派生自StackException(自己定义的异常类)类继承体系中,StackException异常类派生自C++标准库所提供的logic_error类详情查看这个文章,关于logic_error逻辑错误异常类的介绍

  • 而logic_error异常类派生自exception这个标准库中所有异常类继承体系最根本的抽象基类。

  • exception异常类类继承体系有个名为what()的虚函数,返回const char*,用以表示为什么会有异常,而其子类(派生类)会继承what()函数并加以改造称为子类自己的what()函数。

  • 举个例子,关于自己定义的异常类StackException、PopOnEmpty、PushOnFull和其基类logic_error和exception标准库里的异常类

class StackException:public logic_error{
public:
	StackException(const char* what):_what(what){}
	const char* what()const{return _what.c_str();}
protected:
	string _what;
};
class PopOnEmpty:public StackException{
public:
	PopOnEmpty():StackException("Pop on Empty Stack");
	//这个属于父类只声明了带参数的构造函数
	//在这种情况下,子类中的构造函数
	//必须显式调用父类的带参数的构造函数!!
};
class PushOnFull:public StackException{
public:
	PushOnFull():StackException("Push on Full Stack");
};
//以下任何一个catch子句都可以处理比如PushOnFull这种异常
//(栈满还要往栈里压栈)
catch(const PushOnFull &pof)
{
	log(pof.what());
	return;
}
catch(const StackException &stke)
{
	log(stke.what());
	return;
}
catch(const logic_error &lge)
{
	log(lge.what());
	return;
}
catch(const Exception &ex)
{
	log(ex.what());
	return;
}

为什么任何一个catch子句都可以处理比如PushOnFull这种异常?
因为类继承体系的关系,PushOnFull继承自StackException,StackException继承自logic_error,logic_error继承自exception。
只要抛出了PushOnFull异常,可以被四种catch子句捕获并用对应的四种异常类类对象来处理异常+终止函数运行。

踩雷清单

class StackException:public logic_error{
public:
StackException(const char* what):_what(what){}
const char* what()const{return _what.c_str();}
protected:
string _what;
};
class PopOnEmpty:public StackException{
public:
PopOnEmpty():StackException(“Pop on Empty Stack”);//👈
//这个属于父类只声明了带参数的构造函数
//在这种情况下,子类中的构造函数
//必须显式调用父类的带参数的构造函数!!!切记

};

至此,EssentialC++完结!总结这本书的学习

前期记笔记感觉还好,到后面越来越难,记笔记来让自己巩固知识点似乎变得很困难,不过还是坚持了下来。
这本书翻译稍微有点问题,有的地方翻译的感觉不是那回事,有的地方很绕。不过大体内容还不错,层层递进!(数列打天下)
我也大致了解了C++语言的类、继承、多态、模板、STL、重载、异常等特性(后续要跟上C++ primer再学一遍,给它学明白!)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值