文章详情

短信预约-IT技能 免费直播动态提醒

请输入下面的图形验证码

提交验证

短信预约提醒成功

C++中的异常实例详解

2024-04-02 19:55

关注

1. 异常

异常: 异常是面向对象与法处理错误的一种方式

1.1 C语言中处理错误的方式

1.2 C语言处理错误方式的缺陷

//第2点
T& operator[](int index)
{
	//问题: 当访问的下标index超出容器的范围,此时该返回什么值?
    //解决方案: 可以再传一个参数,用于获取真正的结果(当输出型参数), 返回值就用于判断是否正确的返回了. 
    //实际上还是很麻烦
}

2. C++异常

2.1 异常相关关键字

try
{
    //保护代码(可能会抛出异常的代码)
}catch(ExceptionType e1)
{
    //处理异常
}catch(ExceptionType e2)
{
    //处理异常
}catch(ExceptionType eN)
{
    //处理异常
}

2.2 异常的使用

2.2.1 异常的抛出和匹配原则

  1. 异常是通过抛出对象而引发的,该对象的类型决定了应该匹配到哪个catch块
  2. 异常的抛出会去匹配在整个调用链中与该对象类型相匹配且距离最近的那个
  3. 当异常抛出后,执行流会直接跳转到整个调用链当中能catch到异常的位置处,不能catch的地方就不会执行了,所以这可能导致内存泄漏、文件流没关闭、锁未释放**等情况。(free、fclose、unlock未执行)
  4. catch(…)可以用来捕获任意类型对象的异常,一般用于表示"未知异常"或被用作异常的重新抛出时的接收catch块
  5. 在抛出与捕获的类型匹配上并不全都是类型相同才能匹配。我们可以使用基类去捕获 —> 抛出的派生类的对象。 //会在下面具体讲解

· 原则2:函数调用链中的异常 - 栈展开的匹配原则

  1. 查看throw是否在try的内部,如果存在能匹配到的catch语句,就跳转到catch中进行异常的处理。
  2. 如果没有匹配的catch就退出当前栈,去查找调用链当中的能够匹配到的catch
  3. 如果在main函数中都没有找到匹配的catch,则终止程序。

栈展开:上述的这个沿着调用链去逐个栈中查找匹配的catch语句的过程就是栈展开。

image-20211225134740717

//代码演示:f3()中抛出异常,该异常会被f1()捕捉,而在main函数中的catch是无法捕捉到的
void f3()
{
	throw 123;
}
void f2()
{
    f3();
}
void f1()
{
    try
    {
        f2();
        cout << "f1() not catched Exception!" << endl;
    }catch(int& err)
    {
        cout << "f1()-err: " << err << endl;
    }
}

int main()
{
    try
    {
        f1();
        cout << "main not catched Exception!" << endl;
    }catch(int& err)
    {
        cout << "main-err: " << err << endl; 
    }
    return 0;
}

image-20211225110321881

注意:

抛出异常对象后,会生成一个异常对象的拷贝(可能是一个临时对象),这个拷贝的临时对象在被catch后会被销毁。异常的执行流执行顺序:从throw抛出异常处跳转到调用链中能够匹配的catch语句中;然后执行catch块中的代码;catch执行完毕后在当前函数栈中顺序执行。(整个调用链中没有匹配的就终止程序)

2.3 异常的重新抛出

 可能会存在单个catch不能完全处理一个异常的情况,在经过一些校正处理后,我们希望将该异常交给外层调用链中的函数来处理,此时我们可以通过在catch中重新抛出异常的方式把异常传递给调用链的上层函数处理。

//在SecondThrowException()函数中我们要delete[]动态开辟(new出来)的空间,
//如果不使用异常的重新抛出的话,就会造成内存泄漏问题 (也可以使用RAII)
void FirstThrowException()
{
    throw "First throw a exception!";
}

void SecondThrowException()
{
    int* arr = new int[10];
    try
    {
        FirstThrowException();
    }catch(...)
    {
        cout << "Delete[] arr Success!" << endl;
        delete[] arr;
        throw;
    }
}

void SoluteException()
{
    try
    {
        SecondThrowException();
    }catch(const char* err)
    {
        cout << err << endl;
    }
}

int main()
{
    SoluteException();
    return 0;
}

image-20211225154404777

2.4 自定义异常体系

自定义异常体系实际上就是自己定义的一套异常管理体系,很多公司当中都会自定义自己的异常体系以便于规范的进行异常管理。它主要用到了我们在上面所说的一条规则: 我们只需要抛出派生类对象,然后捕获基类对象就可以了。这样的抛出与捕获方式非常便于异常的处理。

class MyException
{
public:
	MyException(string errmsg, int id)
		:_errmsg(errmsg),
		 _id(id)
	{}
	
	virtual string what() const = 0;	//必须放到public下才能让类外定义的成员访问到
protected:
	int _id;		//错误码
	string _errmsg;	//存放错误信息
	//list<StackInfo> _traceStack;	//存放调用链的信息
    //...
};

class CacheException : public MyException
{
public:
	CacheException(string errmsg, int id)
		:MyException(errmsg, id)
	{}

	virtual string what() const
	{
		return "CacheException!: " + _errmsg;
	}
};

class NetworkException : public MyException
{
public:
	NetworkException(string errmsg, int id)
		:MyException(errmsg, id)
	{}

	virtual string what() const
	{
		return "NetworkException!: " + _errmsg;
	}
};

class SqlException : public MyException
{
public:
	SqlException(string errmsg, int id)
		:MyException(errmsg, id)
	{}

	virtual string what() const
	{
		return "SqlException!: " + _errmsg;
	}
};

int main()
{
	try
	{
		//抛出任意的派生类对象
		throw SqlException("sql open failed", 10);
	}
    catch (const MyException& e)	//只需要捕获基类对象
	{
		cout << e.what() << endl;	//这里实际上完成了一个多态
	}
	catch (...)	//走到这里说明出现未知异常
	{
		cout << "Unknown Exception!" << endl;
	}

	return 0;
}

image-20211225153550934

2.5 异常安全

  1. 最好不要在构造函数中抛异常,构造函数是完成对象的构造和初始化的,在里面抛异常可能会导致对象构造不完整或没有完全初始化。 (可能会造成内存泄漏)
  2. 最好不要在析构函数中抛异常,析构函数是完成资源的清理工作的,在里面抛异常可能导致资源没清完就结束了函数调用,从而导致内存泄漏、句柄未关闭等问题。
  3. 注意new、fopen、lock的使用

2.6 异常规范

异常规范的指定是为了让使用者知道函数可能抛出哪些异常,用法:

void func1() throw(); //表示该函数不会抛异常
void func2() noexcept; //等价于throw()  表示该函数不会抛异常
void func3() throw(std::bad_alloc);	//表示该函数只会抛出bad_alloc的异常
void func4() throw(int, double, string); //表示该函数会抛出int/double/string类型中的某种异常

2.7 C++标准库的异常体系

C++提供了一系列标准的异常,定义在中,下面是这些异常的组织形式。

异常描述
std::exception该异常是所有标准 C++ 异常的父类。
std::bad_alloc该异常可以通过 new 抛出。
std::bad_cast该异常可以通过 dynamic_cast 抛出。
std::bad_exception这在处理 C++ 程序中无法预期的异常时非常有用。
std::bad_typeid该异常可以通过 typeid 抛出。
std::logic_error理论上可以通过读取代码来检测到的异常。
std::domain_error当使用了一个无效的数学域时,会抛出该异常。
std::invalid_argument当使用了无效的参数时,会抛出该异常。
std::length_error当创建了太长的 std::string 时,会抛出该异常。
std::out_of_range该异常可以通过方法抛出,例如 std::vector 和 std::bitset<>::operator。
std::runtime_error理论上不可以通过读取代码来检测到的异常。
std::overflow_error当发生数学上溢时,会抛出该异常。
std::range_error当尝试存储超出范围的值时,会抛出该异常。
std::underflow_error当发生数学下溢时,会抛出该异常。
int main()
{
	try
    {
        vector<int> v(5);
        v.at(5) = 0;	//v.at(下标) = v[下标] + 抛异常
    }
    catch(const exception& e)
    {
        cout << e.what() << endl;
    }
    catch(...)
    {
		cout << "Unknown Exception!" << endl;        
    }
}

2.8 异常的优缺点

2.8.1 优点

  1. 清晰的显示出错误信息
  2. 可以很好地解决返回值需要返回有效数据的函数 //如T& operator[ ] (int index)
  3. 在多层函数调用时发生错误,可以用直接在外层进行捕获异常
  4. 异常在很多第三方库中也有使用 //boost、gtest、gmock

2.8.2 缺点

  1. 异常会导致执行流跳转,这会使得调试分析程序时更加困难
  2. C++没有GC (垃圾回收机制),使用异常时可能导致资源泄露等异常安全问题。
  3. C++标准库的异常体系不实用
  4. C++的允许抛出任意类型的异常,在项目中不进行规范管理的话,会十分混乱。 //一般需要定义一套继承体系的异常规范

总结

到此这篇关于C++异常的文章就介绍到这了,更多相关C++异常内容请搜索编程网以前的文章或继续浏览下面的相关文章希望大家以后多多支持编程网!

阅读原文内容投诉

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

软考中级精品资料免费领

  • 历年真题答案解析
  • 备考技巧名师总结
  • 高频考点精准押题
  • 2024年上半年信息系统项目管理师第二批次真题及答案解析(完整版)

    难度     813人已做
    查看
  • 【考后总结】2024年5月26日信息系统项目管理师第2批次考情分析

    难度     354人已做
    查看
  • 【考后总结】2024年5月25日信息系统项目管理师第1批次考情分析

    难度     318人已做
    查看
  • 2024年上半年软考高项第一、二批次真题考点汇总(完整版)

    难度     435人已做
    查看
  • 2024年上半年系统架构设计师考试综合知识真题

    难度     224人已做
    查看

相关文章

发现更多好内容

猜你喜欢

AI推送时光机
位置:首页-资讯-后端开发
咦!没有更多了?去看看其它编程学习网 内容吧
首页课程
资料下载
问答资讯