【c++修行之路】异常

这篇具有很好参考价值的文章主要介绍了【c++修行之路】异常。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

前言

大家好久不见,今天一起来学习一下c++中的异常。

C语言C++对比

C语言在处理错误的时候一般使用assert或者错误码来处理,但二者都相对有局限性
assert:错误自动终止程序,处理方式非常暴力
错误码:只有一个错误码,错误信息相对少,不直观

C++语言提供了一种新的方式解决上面的问题,即异常

异常

throw在可能出现异常的地方使用,意为抛出异常;try/catch在处理异常的地方使用,意为处理异常。

实例代码:

#include <iostream>

using namespace std;

double Div()
{
	int x, y;
	cin >> x >> y;

	if (y == 0)
		throw "div Exception cause div 0!!!";
	else
		return (double)x / (double)y;
}

int main()
{
	try
	{
		Div();
	}
	catch (const char* str)
	{
		cout << "[Exception]:" << str << endl;
	}

	return 0;
}

捕获异常时的注意事项

匹配

1、类型必须匹配,必须严格匹配。
2、在最近的捕获处抛出。

第二点:比如在此处,抛出时离middle最近且严格匹配,所以直接在middle层抛出了。

#include <iostream>

using namespace std;

double Div()
{
	int x, y;
	cin >> x >> y;

	if (y == 0)
		throw "div Exception cause div 0!!!";
	else
		return (double)x / (double)y;
}


void middle()
{
	try
	{
		Div();
	}
	catch (const char* str)
	{
		cout << "[middle-Exception]:" << str << endl;
	}
}


int main()
{
	try
	{
		middle();
	}
	catch (const char* str)
	{
		cout << "[main-Exception]:" << str << endl;
	}

	return 0;
}

细节

异常抛出的时候相当于传值返回,因此捕获到的是异常的一份拷贝构造,因为编译器有很多优化,所以在抛出异常的时候一般都直接抛出一个匿名对象,捕获到的异常是一个局部对象,而不是一个临时对象这点要分清楚。

使用一个Exception类丰富错误信息

使用字符串来表示异常也有很多局限,一般在存储异常信息的时候都是使用一个异常类:

现在我们模拟一个场景:在一个服务中,我们提供网络服务、数据库服务,如果数据库出现错误要看一下特定的sql语句。

结合继承和多态,我们能够设计出更符合实际需求的异常代码:这样也会减少乱抛异常导致系统混乱的可能。顺便一提,catch里加…表示可以捕获任意异常,尽管我们无从得知是什么异常,但可以保证程序其他功能不受限制。

class Exception
{
public:
	Exception(int errid,string errmsg)
		: _errid(errid)
		, _errmsg(errmsg)
	{

	}

	virtual string GetErrMsg() const 
	{
		return _errmsg;
	}

	int GetErrid() const 
	{
		return _errid;
	}


protected:
	int _errid;
	string _errmsg;
};


class SqlException : public Exception
{
public:
	SqlException(int errid, const string& errmsg,const string& sql)
		: Exception(errid,errmsg)
		, _sql(sql)
	{

	}

	virtual string GetErrMsg() const override 
	{
		string msg = "SqlException:";
		msg += _errid;
		msg += "->";
		msg += _sql;
		return msg;
	}
private:
	string _sql;
};


class CacheException : public Exception
{
public:
	CacheException(int errid, const string& errmsg)
		: Exception(errid,errmsg)
	{

	}

	virtual string GetErrMsg() const override
	{
		string msg = "CacheException:";
		msg += _errmsg;
		return msg;
	}

};


class HttpServerException : public Exception
{
public:
	HttpServerException(int errid, const string& errmsg, const string& type)
		: Exception(errid, errmsg)
		, _type(type)
	{

	}


	virtual string GetErrMsg() const override
	{
		string msg = "HttpServerException:";
		msg += "type->";
		msg += _type;
		msg += _errmsg;
		return msg;
	}
private:
	const string _type;
};

void SQLMgr()
{
	srand(time(0));
	if (rand() % 7 == 0)
	{
		throw SqlException(100, "权限不足", "select * from name = '张三'");
	}

	cout << "调用成功" << endl;
}

void CacheMgr()
{
	srand(time(0));
	if (rand() % 5 == 0)
	{
		throw CacheException(100, "权限不足");
	}
	else if (rand() % 6 == 0)
	{
		throw CacheException(101,"数据不存在");
	}

	SQLMgr();
}

void HttpServer()
{
	// 模拟服务出错
	srand(time(0));
	if (rand() % 3 == 0)
	{
		throw HttpServerException(100, "请求资源不存在", "get");
	}
	else if (rand() % 4 == 0)
	{
		throw HttpServerException(101, "权限不足", "post");
	}

	CacheMgr();
}

int main()
{
	while (1)
	{
		this_thread::sleep_for(chrono::seconds(1));

		try
		{
			HttpServer();
		}
		catch (const Exception& e) // 这里捕获父类对象就可以
		{
			// 多态
			cout << e.GetErrMsg() << endl;
		}
		catch (...)
		{
			cout << "Unkown Exception" << endl;
		}
	}

	return 0;
}

异常的重新抛出

在这样的场景下:假如在func函数中要记录下来每一次div的结果,但如果发现除0错误后会引发异常,导致动态内存分配的空间没有释放,因此需要在这一层捕获异常并释放空间,但我么又希望异常能在外层统一处理,所以引入了重新抛出异常的概念,代码如下:

double Div()
{
	int x, y;
	cin >> x >> y;
	

	if (y == 0)
		throw "div Exception cause div 0!!!";
	else
		return (double)x / (double)y;
}

void func()
{
	int* arr = new int[20];
	try
	{
		Div();
	}
	catch (const char* str)
	{
		delete[] arr;
		throw;
	}	
	
	delete[] arr;
}


int main()
{
	
	try
	{
		func();
	}
	catch (const char* str)
	{
		cout << str << endl;
	}


}

c++98和c++11的异常建议

在c++98中,提供了指明异常的方式,但这种方式其实非常没有用,形同虚设,因为你在抛出异常的时候可以不采用这种方式,就算采用这种方式,也会有一些其他问题。

throw([e1],[e2]) 表示可能会抛出的异常

比如:
说明不抛出异常的类仍然可以抛出并且程序不会自己终止,而指明异常的可以抛出没有指明的异常,这使得这种方式非常没用。

void Noexception() throw()
{
	throw "你好";
}

void hasexception() throw(SqlException, HttpServerException)
{
	throw "helloworld";
}



int main()
{
	try
	{
		hasexception();
	}
	catch (const char* str)
	{
		cout << str << endl;
	}
	
}

c++11中提供了一种新的异常方案:
明确不抛出异常的话,可以加上noexcept关键字,如果可能抛出异常,就不需要加此关键字。

void Noexception() noexcept
{
	throw "你好";
}

这样的写法就会直接报错。最好不要在构造函数和析构函数抛出异常,因为这样有可能会导致构造/析构不完全,从而引发一系列的问题。像这样的场景还包括文件操作、上锁解锁等。

异常的优缺点

其实我们能够感受到异常带来的诸多便利,简单介绍一些:
1、使得错误信息更加清晰和丰富,一目了然。
2、相比于错误码需要层层返回错误码,异常会直接跳到最外面捕获的位置。
3、有些特殊场景只能抛出异常,比如有的函数没有返回值,容器越界等(数组越界是未定义的行为,不会抛出异常) 没有返回值的场景。

但有利必有弊,异常也有一些缺点:
1、执行流乱跳,这让我们在断点调试时非常难受。
2、库的异常体系不好,导致大家经常各用各的,很难统一。
3、需要我们规范使用,但这种规范又不强制,导致形同虚设。
4、c++没有垃圾回收机制,很容易出现内存泄漏和死锁等安全问题。

但总的来说,异常的利大于弊,是未来语言处理错误的趋势,我们仍然要学好异常。

结语

今天的内容就到这里了,我们下次再见~。文章来源地址https://www.toymoban.com/news/detail-522962.html

到了这里,关于【c++修行之路】异常的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 【高级程序设计语言C++】异常与智能指针

    当我们编写程序时,可能会遇到各种错误和异常情况,例如除以零、访问无效的内存地址等。为了能够处理这些异常情况,C++提供了异常处理机制。 异常是程序在运行时发生的意外或错误情况。当异常发生时,程序会中断当前的执行流程,并跳转到异常处理代码块。异常处理

    2024年02月10日
    浏览(31)
  • [开发语言][C++]:递增递减运算符

    递增运算符和递减运算符为对象的+1和-1提供了简洁的书写形式。 自增自减运算符的应用: 这两个运算符除了应用在算术运算,还可应用于迭代器,因为很多迭代器并不支持算术运算。 递增和递减运算符有两种书写形式:前置版本和后置版本。 前置版本 ++i --i :首先将运算

    2024年01月25日
    浏览(38)
  • 《数据结构、算法与应用C++语言描述》-列车车厢重排问题

    完整可编译运行代码见:Github::Data-Structures-Algorithms-and-Applications/_10Train_carriages_rearrangement/ 一列货运列车有 n 节车厢,每节车厢要停靠在不同的车站。假设 n个车站从 1 到n 编号,而且货运列车按照从n到1的顺序经过车站。车厢的编号与它们要停靠的车站编号相同。为了便于从

    2024年04月10日
    浏览(50)
  • 【VisualStudio】使用 C++ 语言开发 Qt 环境配置教程

    知识不是单独的,一定是成体系的。更多我的个人总结和相关经验可查阅这个专栏:Visual Studio。 先上一张效果图,具体步骤主要分为以下三步。 这一步不再赘述,注意一定要安装 C++ 语言。 可以参考这个教程 Visual Studio 2022安装与使用教程。 这一步也不再赘述,网上搜索教

    2024年02月10日
    浏览(63)
  • [开发语言][c++]:Static关键字和全局变量

    写在前面: 如果您只是想回顾或了解一下static和全局变量的异同点,那么下面的总结将满足您的需求。 如果您是一位初学者或对二者的使用模棱两可,建议您读完该篇文章,相信这一篇将解答完您对于static以及全局变量的所有疑惑。 如有问题或建议欢迎评论 or 私信

    2024年01月20日
    浏览(43)
  • 第七十七篇:车辆安全-车载软件C++语言开发指南(AUTOSAR C++)

    C++是面向对象的编程,比C语言更加复杂,抽象程度高,但C++在一些图像处理、系统、控件的编程方面,实用性更强,具有自己的编程优势。在车载嵌入式系统的开发中,C和C++都具有重要的作用。C++语言所使用的面向对象的编程技术如封装、继承和多态性极大的提高了在大规

    2024年02月04日
    浏览(49)
  • 机器人寻路算法双向A*(Bidirectional A*)算法的实现C++、Python、Matlab语言

    最近好久没更新,在搞华为的软件挑战赛(软挑),好卷只能说。去年还能混进32强,今年就比较迷糊了,这东西对我来说主要还是看运气,毕竟没有实力哈哈哈。 但是,好歹自己吭哧吭哧搞了两周,也和大家分享一下自己的收获吧,希望能为后来有需要的同学提供一些帮助

    2024年04月13日
    浏览(34)
  • [开发语言][c++][python]:C++与Python中的赋值、浅拷贝与深拷贝

    写在前面 :Python和C++中的赋值与深浅拷贝,由于其各自语言特性的问题,在概念和实现上稍微有点差异,本文将这C++和Python中的拷贝与赋值放到一起,希望通过对比学习两语言实现上的异同点,加深对概念的理解。 C++中所谓的 浅拷贝 就是由(系统默认的) 拷贝构造函数对

    2024年02月02日
    浏览(38)
  • [开发语言][python][c++]:C++中的this指针和Python中的Self -- 26岁生日

    以朋友的新岁祝福开篇,祝笔者也祝大家☺️: 之前一直对 C++ 中的 this 和 python 中的 self 到底是什么关系,为什么 C++ 要显式的写出来,python 则不需要? 模糊不清,趁着周末整理一下相关结论,希望本篇文章可以解答这些问题,同时对C++和Python中的类加深些理解。 python 当

    2024年01月24日
    浏览(53)
  • 【华为OD机试真题 C++语言】68、矩阵扩散 | 机试题+算法思路+考点+代码解析

    🍂个人博客首页: KJ.JK   🍂专栏介绍: 华为OD机试真题汇总,定期更新华为OD各个时间阶段的机试真题,每日定时更新,本专栏将使用C++语言进行更新解答,包含真题,思路分析,代码参考,欢迎大家订阅学习 🎃题目描述 存在一个m*n的二维数组,其成员取值范围为0或1

    2024年02月16日
    浏览(43)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包