C++11之异常

一、C++异常概念

  • 异常是一种处理错误的方式。当一个函数发现自己无法处理的错误时就可以抛出异常。让函数的直接或间接的调用者处理这个错误。

1.关键字

  • throw:抛出异常的关键字。
  • catch:捕获异常的关键字。
  • try:try块中的代码标识被激活的特定异常,后面通常跟着一个或多个catch。

2.异常终止程序执行

#include <iostream>
#include <windows.h>

using namespace std;
double Division(int a, int b)
{
	//当b==0时抛异常,抛出的异常可以为任意类型对象
	if (b == 0)
	{
		throw "除0错误";
	}
	else
	{
		return (double)a / (double)b;
	}
}
void func()
{
	int a, b;
	cin >> a >> b;

	cout << Division(a, b) << endl;
}
int main()
{
	//抛出异常一定要捕获,且一般在最外层进行捕获
	try
	{
		func();
	}
	//捕获字符串类型异常
	catch (const char* errostr)
	{
		cout << errostr << endl;
	}
	//捕获任意没有匹配类型的异常
	catch (...)
	{
		cout << "Unkown Error" << endl;
	}

	return 0;
}

在这里插入图片描述

3.重新抛异常

void func()
{
	int* array = new int[10];
	int a, b;
	cin >> a >> b;
	cout << Division(a, b) << endl;

	delete[] array;
}
  • 上述代码存在异常安全问题 ==》内存泄漏。因为抛出异常之后,就不会执行后面的delete[] array这句代码。
  • 解决方法:1.中间捕获异常处理完异常之后再抛异常。(再抛出异常的理由是一般异常处理都是在最外层代码处理的)
  • 如下所示:
#include <iostream>
#include <windows.h>

using namespace std;
double Division(int a, int b)
{
	//当b==0时抛异常,抛出的异常可以为任意类型对象
	if (b == 0)
	{
		throw "除0错误";
	}
	else
	{
		return (double)a / (double)b;
	}
}
void func()
{
	int* array = new int[10];
	try
	{
		int a, b;
		cin >> a >> b;
		cout << Division(a, b) << endl;
	}
	//中间捕获异常一般写成catch(...)
	catch (...)
	{
		delete[] array;
		throw;
	}
	delete[] array;
}
int main()
{
	//抛出异常一定要捕获,且一般在最外层进行捕获
	try
	{
		func();
	}
	//捕获字符串类型异常
	catch (const char* errostr)
	{
		cout << errostr << endl;
	}
	//捕获任意没有匹配类型的异常
	catch (...)
	{
		cout << "Unkown Error" << endl;
	}

	return 0;
}

二、异常安全

  • 构造函数完成对象的构造和初始化,最好不要在构造函数中抛出异常,否则可能导致对象不完整或者没有完全初始化。
  • 析构函数完成资源的清理,最好不要在析构函数中抛出异常,否则可能导致资源泄露(内存泄露、句柄未关闭等情况。)
  • new和delete之间抛异常注意可能会导致内存泄漏。
  • lock和unlock之间抛异常可能会导致死锁。

三、异常规范

  • 异常规格说明是为了让函数使用者知道该函数可能抛出的异常有哪些。可以在函数后面接throw(类型),列出该函数可能抛掷的所有异常的类型。
  • 函数后面接throw(),表示该函数不抛异常。
  • 若没有异常接口声明,表示该函数可以抛出任何类型的异常。
//表示该函数会抛出char*/int/double中的某种类型的异常
void func() throw(char*, int, double);

//表示该函数只会抛出bad_alloc的异常
void* operator new(size_t size) throw(bad_alloc);

//表示该函数不会抛出异常
void func() throw();

四、自定义异常体系

  • 因为异常可以抛出任意类型的异常,也就是说捕获的地方也得各种类型都捕获,那么这代码就很难控制,故在工程中不会让抛出任意类型异常,而是进行一定程度的异常的规范处理。实际中都会定义一套继承的规范体系,这样大家抛出的异常都是派生类对象,捕获一个基类就可以了。
#include <iostream>
#include <string>
#include <windows.h>

using namespace std;

class Exception
{
public:
	Exception(const char* err, int id)
		:_err(err)
		,_id(id)
	{}
	virtual void Print() const = 0;

protected:
	string _err;
	int _id;
};
class SqlException :public Exception
{
public:
	SqlException(const char* err, int id)
		:Exception(err, id)
	{}
	void Print()const 
	{
		cout << "SqlException" << _err << "+" << _id << endl;
	}
};
class HttpException :public Exception
{
public:
	HttpException(const char* err, int id)
		:Exception(err, id)
	{}
	void Print()const 
	{
		cout << "HttpException" << _err << "+" << _id << endl;
	}
};

void Start()
{
	if (rand() % 3 == 0)
		throw SqlException("Sql语句错误", 1);
	if (rand() % 5 == 0)
		throw HttpException("HttpException语句错误", 2);

	::Sleep(1000);//等待1s
}
int main()
{
	while (1)
	{
		try
		{
			//抛出的异常都是子类对象抛出的
			Start();
		}
		//这里捕获异常用父类对象就可以捕获所有子类对象抛出的异常
		catch (const Exception& e)
		{
			e.Print();//多态
		}
		catch (...)
		{
			cout << "Unknown Error" << endl;
		}
	}

	return 0;
}

五、C++标准库的异常体系

#include <iostream>
#include <string>
#include <vector>
#include <windows.h>

using namespace std;

void func()
{
	if (rand() % 3 == 0)
		char* p = new char[0x7fffffff];//new申请空间失败会抛异常,new抛出的异常是bad_alloc
	if (rand() % 5 == 0)
	{
		vector<int> v = { 0, 1, 2 };
		v.at(3);//“at()”下标访问越界以后抛异常
	}

	::Sleep(1000);
}
int main()
{
	while (1)
	{
		try
		{
			func();
		}
		catch (const exception& e)
		{
			//what()输出异常信号
			cout << e.what() << endl;
		}
		catch (...)
		{
			cout << "Unknown Error" << endl;
		}
	}

	return 0;
}

六、异常的优缺点

1.异常的优点

  • 异常对象定义好了,相比错误码的方式可以清晰准确的展示出错误的各种信息,甚至可以包含堆调用的信息。这样就可以帮助更好的定位程序的bug。
  • 返回错误码的方式有个很大的问题就是,在函数调用链中,深层的函数返回了错误,我们得到层层返回的错误,到最外层才能拿到错误,而程序不会在异常那停止运行。
  • 很多的第三方库都包含异常,比如boost、gtest等常用的库,那么我们使用它也需要使用到异常。
  • 很多的测试框架都使用异常,这样能更好的进行单元测试等白盒测试。
  • 部分函数使用异常更好处理,比如构造函数没有返回值。不方便使用错误码方式处理。

2.异常的缺点

  • 异常会导致程序的执行流乱跳,且混乱,运行时出错抛出异常就会乱跳,会影响我们跟踪调试和分析程序。
  • 异常会有一些性能的开销。
  • C++没有垃圾回收机制,资源需要自己管理。有了异常非常容易导致内存泄漏、死锁等异常安全问题。
  • C++标准库的异常体系定义得不好。
  • 异常尽量规范使用:1.抛出异常类型都继承自一个基类。2.函数是否抛异常,抛什么异常,都使用func() throw();的方式规范化。
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值