【C++】Essential c++第七章学习笔记

Essential c++ 第七章

  第七章主要介绍了如何使用异常来使你的程序更安全。

1.异常的抛出

  异常抛出使用throw关键词进行,当函数中出现某个错误后,会抛出某个标志物。这个标志物可以是简单的值类型,也可以是复杂的符合类型。注意,当程序运行throw时,其后面的程序都会终止进行。比如本例子中,如果num大于max,最后一句创建成功是不会被运行的。

  抛出int


//例一 抛出int
void f1(int num, int max = 50)
{
	if (num > max)
		throw 42;
	cout << "创建成功" << endl;
}

  抛出string,注意,catch解析这个错误的时候,使用const char*类型接受

//例二 抛出string
void f2(int num, int max = 50)
{
	if (num > max)
		throw "输入越界";
	cout << "创建成功" << endl;
}

  抛出class有两种写法,一种是不抛出具体实例,一种是建立实例以后再抛出实例


//例三 抛出class的两种写法
class error
{
public:
	error(int num,int max):_num(num),_max(max){}
	void what()
	{
		cout << _num << "超过了最大值" << _max << endl;
	}
private:
	int _num;
	int _max;
};
void f3(int num, int max = 50)
{
	if (num > max)
	{
		throw error(num, max);
	}
	cout << "创建成功" << endl;
}

void f4(int num, int max = 50)
{
	if (num > max)
	{
		error err(num, max);
		throw err;
	}
	cout << "创建成功" << endl;
}

2.异常的处理

  异常的处理使用try-catch语句进行。try-catch-throw是一组常用的异常处理关键词。通过try中运行可能出错的程序,throw抛出异常,catch接受throw抛出的异常,从而进行处理。我们针对上面的四种错误抛出,定义四种try-catch语句。当try语句某句话抛出异常时,这个语句及其后面的程序都将不再进行。

2.1 try-catch

2.1.1 捕获属于catch的异常

  当try中抛出异常的时候,下面的catch会进行一一匹配,找到抛出的异常类型,然后运行相应的catch中的程序。

  处理抛出的int

//01 例程1
	cout << "抛出int" << endl;
	try
	{
		f1(100, 50);
	}
	catch (int len)
	{
		cout << len << endl;
		
	}
	cout << endl;

  处理抛出的string

//02 例程2
	cout << "抛出string" << endl;
	try
	{
		f2(100, 50);
	}
	catch (const char* str)
	{
		cout << str << endl;

	}
	cout << endl;

  处理抛出的error类

//03 例程3-1
	cout << "抛出class1" << endl;
	try
	{
		f3(100, 50);
	}
	catch ( error& err)
	{
		err.what();

	}
	cout << endl;

	//03 例程3-2
	cout << "抛出class2" << endl;
	try
	{
		f4(100, 50);
	}
	catch (error & err)
	{
		err.what();

	}
	cout << endl;

2.1.2 在父类中查找异常种类

  当然也会存在抛出的异常不在catch中的情况,这个时候,catch会检测,被抛出的异常的父类是否能够与自己匹配,如果能,就会执行相应的catch

  我们定义相应的异常类和抛出异常的函数

// 从父类中寻找异常类型
class error1:public error
{
	error1(int num,int max):error(num,max){}
};
void f5(int num, int max = 50)
{
	if (num > max)
	{
		error err1(num, max);
		throw err1;
	}
	cout << "创建成功" << endl;
}

  使用catch捕获异常,不过函数抛出的是error1,而catch中写的是error,但是仍然能够正确运行,就是因为catch发现没有可以匹配的异常类型,但是发现error1的父类就行error,所以接受error异常类型的catch,就能够接受error1异常类型,然后执行catch

//01 从父类寻找异常类型
	cout << "从父类寻找异常类型" << endl;
	try
	{
		f5(100, 50);
	}
	catch (error & err)
	{
		err.what();

	}
	cout << endl;

2.1.3 捕获全部异常

  还有一种写法,能够捕获所有的异常,就是catch(…)


try
{
    //something
}
catch(...)
{
    //捕获所有异常
}

举例

cout << "注意事项一,catch不能有包含关系" << endl;
	try
	{
		f4(100, 50);
	}
	catch (...)
	{
		cout << "error!" << endl;

	}
	cout << endl;
catch (error & err)
	{
		err.what();

	}
2.1.4 注意
  • catch语句不能有相互包含

  如果catch语句相互包含,程序会运行错误,因为不知道该使用哪个catch了,比如我们把catch(…)放到前面,这个时候,后面的catch语句无论如何也不能执行,就被屏蔽掉了,程序是无法执行的。

cout << "catch(...)全部抓住" << endl;
	try
	{
		f4(100, 50);
	}
	catch (...)
	{
		cout << "error!" << endl;

	}
	cout << endl;

  • 构造函数不能抛出异常

  也不能在构造函数中抛出异常,尤其是构造函数中使用new的时候。因为,构造函数如果抛出异常,意味着对象没有定义成功,但是内存以及被分配了,对象没定义,意味着不会执行析构函数,所以会造成内存泄露.

2.2 throw

  如果当前层级的catch不能很好的处理异常,可以使用throw,将异常往上一级抛出,让上一级的catch函数进行处理。只写一个throw;即可,不需要重复再写throw的内容。而且,只有在catch中,才可以使用throw继续往上一层抛出异常

2.3 沿着函数调用链上传

  如果这个函数抛出异常的时候,这个位置没有catch函数,异常会沿着调用他的函数继续往上抛出,一直到有一层有了try-catch语句。不过,如果异常抛出到main层,还不能被处理,程序会被强制终止。

void level1()
{
	throw 1;
}
void level2()
{
	level1();

}

int main()
{
    cout << "沿着调用链上传异常" << endl;
	try
	{
		level2();
	}
	catch (int len)
	{
		cout << len << endl;
	}
	cout << endl;
	
	return 0;
}

2.4 异常的处理流程

  编译器对try中抛出异常的综合的处理过程是

  • 先检查catch中是否有符号的异常类型。
  • 如果没有,检查他的父类有没有这种异常类型,如果有进行处理
  • 如果都没有会把异常向他的调用者抛出
  • 如果一直到main函数都没有人能够处理这种异常,那么程序会被终止。

3.标准异常

  除了我们自己定义的异常以外,c++还有自己内置的异常类,这些异常被称为标准异常。

3.1 举例 bad_alloc

  比如bad_alloc会在new不能提供足够内存大小的时候被抛出,这种异常一般定义在标准函数库中。

3.2 抽象类exception

  所有的标准库异常都来源于抽象类exception,exception类中有一个叫做what的虚函数,返回值为const char*,用来记录具体是哪个标准异常被抛出。

3.3 通过继承exception定义自己的异常

  我们自定义异常的时候,可以继承exception类作为父类,这样的话,catch中写exception,即可以捕获所有类型的异常,并且能够找出他们的名字。如果想要继承exception,注意两点

  • 必须重载虚函数what
  • 必须包括头文件exception

  这里要提两个技巧,一个是如何把多种混杂类型的串变成字符串,另外一个是如何把string变成const char*类型

继承标准异常库定义自己的异常

class exceptionError:public exception
{
public:
	exceptionError(int num,int max):_num(num),_max(max){}
	const char* what()
	{
		ostringstream ex_msg;
		static string msg;

		//技巧一 把混合数据类型的串变成字符串
		ex_msg << _num << "超过最大值" << _max << "!" << endl;
		msg = ex_msg.str();

		//技巧二 把字符串变成 const char *
		return msg.c_str();
	}
private:
	int _num;
	int _max;
};

void throwExceptionError(int num,int max=50)

{
	if (num > max)
	{
		throw exceptionError(num, max);
	}
	cout << "创建成功" << endl;
	
}

使用自己定义的异常

cout << "继承标准异常库" << endl;
	try
	{
		throwExceptionError(100, 50);
	}
	
	catch (exception & exp)
	{
		exp.what();
	}

	cout << endl;

4. 局部资源管理

  下面这个例子,不妨从异常的角度来看看哪里错了

extern Mutex m;
void f()
{
    int *p = new int ;
    m.acquire();
    
    process(p);
    
    m.release();
    delete p;
}

  看似这个函数很正常,但是一旦process函数抛出了异常,后面的内存释放函数就不能运行了,就会造成内存泄露,如何避免这个问题呢

4.1 使用try语句

  可以用try-catch语句来避免内存泄露

extern Mutex m;
void f()
{
try
{
    int *p = new int ;
    m.acquire();
    
    process(p);
    
    m.release();
    delete p;
}
catch(...)
{
     m.release();
    delete p;
}
    
}

  这样虽然是可行的,但是这么写,未免太啰嗦了,其实还有其他的解决方案

4.2 在初始化阶段进行资源的请求

  我们可以把内存类型的变量定义在类的构造函数中,这样即使中间的process函数抛出了异常,也能够通过析构函数释放内存。因为析构函数保证,会在抛出异常结束程序运行之间执行完毕,能够保证不发生内存泄露

void f()
{
    auto_ptr<int> p(new int);
    MutexLock m1(m);
    process();
    //其他什么都不用写了,如果process抛出异常,析构函数会悄悄的执行
}

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值