C++11中异常处理机制详解

目录
  • 一、异常的引入
  • 二、C++异常的关键字
  • 三、异常的抛出与处理规则
  • 四、异常缺陷的处理
  • 五、自定义异常体系
  • 六、异常规范
  • 七、异常安全
  • 八、异常的优缺点
    • 1.优点
    • 2.缺点

一、异常的引入

传统的C语言处理异常的方式有两种:

1.终止程序:使用assert断言语句,如果发生内存错误等,比如内存泄漏或者除0错误,都会直接终止程序。

2.返回错误码:通过错误码判断发生的异常的类型是什么,如系统的很多库的接口程序通过把错误码放到errno中,表示错误。

在实际中的C语言程序基本都是通过返回错误码的方式来处理错误的,部分情况下使用终止程序来处理比较严重的错误。

二、C++异常的关键字

目前市面上的大部分的主流语言都是使用异常机制来处理错误的,当一个函数发现自己无法处理一些错误的时候会进行抛异常处理,让函数直接跳转到捕获异常的地方去处理异常。

下面介绍C++处理异常的几个关键字:

throw:当问题出现的时候,程序会抛出一个异常,这是通过throw关键字来完成的。

catch:通过异常处捕获异常,catch块主要用于处理异常,或者执行一些其他的操作。

try:try块中的代码标识将被激活的特定异常,它的后面一般会跟一个catch块。

三、异常的抛出与处理规则

double Disvision(int a, int b)
{
    if (b == 0)
    {
        throw  "Disvision by zero condition";
    }
    else
    {
        return ((double)a / (double)b);
    }
}
void Func()
{
    int len, time;
    cin >> len >> time;
    cout << Disvision(len, time) << endl;
}
int main()
{
    try
    {
        Func();
    }
    catch(const char* errmsg)
    {
        cout << errmsg << endl;
    }
    catch (...)
    {
        cout << "unkown exception" << endl;
    }
    return 0;
}

当向time传入的值为0的时候,调用throw抛出异常,catch会捕获到该异常进行处理。

1.异常是通过抛出对象而引发的,该对象的类型决定了应该激活哪一个catch的处理代码。

2.被选中的处理代码是调用链中与该对象类型匹配且离抛出异常位置最近的一个。

在这段程序中,有一个调用链:main()->Func()->Disvision(),在Disvision中抛出异常就会沿着调用链寻找能捕获异常的catch来执行。假设Func()中有一个与main()中一模一样的catch函数,那么除0的异常就会优先调用Func()中的catch,因为它离着最近。

3.catch的异常其实是抛出对象的拷贝,因为真正抛出的对象在出作用域的时候就已经被销毁了,拷贝的对象在catch成功之后也会自动销毁。

4.catch(…)可以捕获任意类型的异常,但问题是不知道异常错误是什么。

5.实际中抛出和捕获的匹配原则有一个例外,并不都是类型完全匹配,可以抛出的派生类对象,使用基类捕获。(要引入多态)

6.当catch异常之后,会沿着catch后的子句继续执行,类似goto,算是异常的一个缺陷。

四、异常缺陷的处理

double Disvision(int a, int b)
{
    if (b == 0)
    {
        throw  "Disvision by zero condition";
    }
    else
    {
        return ((double)a / (double)b);
    }
}
void Func()
{
    int* array = new int[10];
    int len, time;
    cin >> len >> time;
    cout << Disvision(len, time) << endl;
    delete[] array;//一旦抛出异常,这里就不会被执行
}
int main()
{
    try
    {
        Func();
    }
    catch(const char* errmsg)
    {
        cout << errmsg << endl;
    }
    catch (...)
    {
        cout << "unkown exception" << endl;
    }
    return 0;
}

对于这段代码而言,一旦Division抛出异常就会立刻执行主函数中的catch,delete释放内存就不会被执行。因此我们需要对这种情况进行处理,即在Func中也捕获一次异常,但是不对异常进行处理,只是释放空间:

void Func()
{
    int* array = new int[10];
    int len, time;
    cin >> len >> time;
    try {
        cout << Disvision(len, time) << endl;
    }
    catch (const char* errmsg)
    {
        cout << "释放空间" << endl;
        delete[] array;
        throw;
    }
}

在释放空间之后,直接调用throw表示将捕获到的异常再抛出去。

五、自定义异常体系

class Exception
{
public:
	Exception(const string& errmsg, int id)
		:_errmsg(errmsg)
		, _id(id)
	{}

	virtual string what() const
	{
		return _errmsg;
	}
protected:
	string _errmsg;
	int _id;
};

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

	virtual string what() const
	{
		string str = "SqlException:";
		str += _errmsg;
		str += "->";
		str += _sql;
		return str;
	}

private:
	const string _sql;
};

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

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

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

	virtual string what() const
	{
		string str = "HttpServerException:";
		str += _type;
		str += ":";
		str += _errmsg;
		return str;
	}

private:
	const string _type;
};

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

	//throw "xxxxxx";
}

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();
}

void ServerStart()
{
	while (1)
	{
		this_thread::sleep_for(chrono::seconds(1));//休眠1s
		try {
			HttpServer();
		}
		catch (const Exception& e) // 这里捕获父类对象就可以
		{
			// 多态
			cout << e.what() << endl;
		}
		catch (...)
		{
			cout << "Unkown Exception" << endl;
		}
	}
}
int main()
{
	ServerStart();
	return 0;
}

这里使用随机数进行模拟异常的抛出类型,我们使用父类捕获异常,抛出子类异常,使用多态调用子类中的what()函数。

六、异常规范

1.异常规格说明的目的是为了让函数使用者知道。可以在函数的后面接throw类(类型),列出这个函数可能抛的所有异常类型。

2.函数后面接throw(),表示函数不抛异常。

3.若无异常接口声明,则此函数可以抛掷任何类型的异常。

void func() throw(A, B, C, D);//只会抛A/B/C/D中的某种类型的异常
void* operator new(size_t size) throw(bad alloc);//这里表示这个函数只会抛出bad_alloc的异常
void* operator new(size_t size, void* ptr) throw();//这个函数不会抛异常

在C++11中,引入了noexception

bool Compare(int x, int y) noexcept(noexcept(x > y))  //C++11
{
    return x > y;//表示如果x > y不发生异常,则Compare函数不会发生异常。
}

七、异常安全

构造函数完成对象的初始化,最好不要在构造函数中抛异常,否则可能导致对象不完整,或没有完全初始化。

析构函数主要完成资源的清理,最好不要在析构函数中抛异常,否则可能导致资源泄漏(内存泄漏,句柄未关闭)。

C++异常经常会导致资源泄漏的问题,比如在new和delete中抛出了异常,导致内存泄漏,在lock和unlock之间抛出了异常导致死锁,C++经常使用RAII来解决以上问题,关于RAII我们在智能指针中讲解。

八、异常的优缺点

1.优点

1.相比错误码,更加清晰展示出错信息。

2.很多库中包含异常,boost,gtest,gmock等常用的库,使用它们也需要异常。

3.部分函数使用异常更好处理,比如构造函数没有返回值,不方便使用错误码方式处理,比如T&operator这样的函数,只有一个返回值,如果pos越界了只能使用异常或者终止程序处理,没办法通过返回值表示错误。

2.缺点

1.异常类似goto,会导致程序的执行流乱跳,并且非常混乱。

2.C++没有垃圾回收机制,可能导致内存泄漏。

3.各个公司的异常体系不同,有一定的学习成本。

到此这篇关于C++11中异常处理机制详解的文章就介绍到这了,更多相关C++11异常处理内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • C++中的异常处理机制详解

    异常处理 增强错误恢复能力是提高代码健壮性的最有力的途径之一,C语言中采用的错误处理方法被认为是紧耦合的,函数的使用者必须在非常靠近函数调用的地方编写错误处理代码,这样会使得其变得笨拙和难以使用.C++中引入了异常处理机制,这是C++的主要特征之一,是考虑问题和处理错误的一种更好的方式.使用错误处理可以带来一些优点,如下: 错误处理代码的编写不再冗长乏味,并且不再和正常的代码混合在一起,程序员只需要编写希望产生的代码,然后在后面某个单独的区段里编写处理错误的嗲吗.多次调用同一个函数,则只需要某个

  • C++之异常处理详解

    程序中的错误分为编译时的错误和运行时的错误.编译时的错误主要是语法错误,比如:句尾没有加分号,括号不匹配,关键字错误等,这类错误比较容易修改,因为编译系统会指出错误在第几行,什么错误.而运行时的错误则不容易修改,因为其中的错误是不可预料的,或者可以预料但无法避免的,比如内存空间不够,或者在调用函数时,出现数组越界等错误.如果对于这些错误没有采取有效的防范措施,那么往往会得不到正确的运行结果,程序不正常终止或严重的会出现死机现象.我们把程序运行时的错误统称为异常,对异常处理称为异常处理.C++中所

  • 深入了解C++异常处理

    目录 基本的异常处理 怎么抛出异常 捕获和处理异常 不存在异常的描述 --- 标识性作用     删减符 ... 异常处理中的传参操作  --- 可以写一个变量进去 可以抛出自己类的对象 标准库当中的异常类 引发标准库中内存申请失败的异常 基本的异常处理 异常处理机制:暂缓问题处理,不在当前函数中处理,在他的调用者中处理(先上车,后补票) 什么是异常:任何东西都可以认为是异常,错误只是异常的一种 异常一旦被抛出,不做处理,如果引发异常,会调用默认abort函数终止程序 捕获和处理异常: thro

  • C++学习笔记之浅谈异常处理

    异常处理主要是针对能通过编译但是运行是在某个特定条件下会出现异常,程序崩溃,结果出错.来进行的东西 C++处理异常的机制是由3个部分组成的,即检查(try).抛出(throw)和捕捉(catch).把需要检查的语句放在try块中,throw用来当出现异常时发出一个异常信息,而catch则用来捕捉异常信息,如果捕捉到了异常信息,就处理它. try {被检查的语句} catch(异常信息类型 [变量名]) {进行异常处理的语句} 粘一个简单的异常处理的题: 如果三角形满足三角形内角和大于第三边才会有

  • 详细分析C++ 异常处理

    异常是程序在执行期间产生的问题.C++ 异常是指在程序运行时发生的特殊情况,比如尝试除以零的操作. 异常提供了一种转移程序控制权的方式.C++ 异常处理涉及到三个关键字:try.catch.throw. throw: 当问题出现时,程序会抛出一个异常.这是通过使用 throw 关键字来完成的. catch: 在您想要处理问题的地方,通过异常处理程序捕获异常.catch 关键字用于捕获异常. try: try 块中的代码标识将被激活的特定异常.它后面通常跟着一个或多个 catch 块. 如果有一个

  • C++11中异常处理机制详解

    目录 一.异常的引入 二.C++异常的关键字 三.异常的抛出与处理规则 四.异常缺陷的处理 五.自定义异常体系 六.异常规范 七.异常安全 八.异常的优缺点 1.优点 2.缺点 一.异常的引入 传统的C语言处理异常的方式有两种: 1.终止程序:使用assert断言语句,如果发生内存错误等,比如内存泄漏或者除0错误,都会直接终止程序. 2.返回错误码:通过错误码判断发生的异常的类型是什么,如系统的很多库的接口程序通过把错误码放到errno中,表示错误. 在实际中的C语言程序基本都是通过返回错误码的

  • Golang中异常处理机制详解

    前言 通常我们需要编写好的错误处理方式,在了避免某些程序员滥用异常,于是Go这里直接把异常这一块给砍掉了,最终还是通过返回值来判断程序的异常情况,毕竟Go可是支持多返回值的语言,比如atoi.itoa等函数,就不能忽略它的第二个返回值,因为第二个返回值代表了转换是否成功!不过Golang还是提供了一些错误处理机制的 Go的错误机制 1.没有异常机制 2.error类型实现了error接口 3.可以通过errors.New来快速创建错误实例 type error interface{ Error(

  • java-流的使用完结与异常处理机制(详解)

    1.1 java.io.objectInputStream 对象输入流:用于将一组字节(通过对象输出流写出对象而转换的一组字节)读取并转换为对应的对象.对象输出流将对象写出时转换为一组字节的过程,称为:对象序列化对象输入流将这组字节读取并还原会对象的过程,称为:对象反序列化 1.2 java.io.Serializable Serializable序列化接口 当一个类实现了Serializable接口后,应当在当前类中添加一个常量: 序列化版本号serialVersionUID 序列化版本号若不

  • Java try-catch-finally异常处理机制详解

    Java中的try-catch-finally异常处理 一.异常处理 异常(Exception):是在运行发生的不正常情况. 原始异常处理: if(条件) { 处理办法1 处理办法2 处理办法3 } if(条件) { 处理办法4 处理办法5 处理办法6 } 代码阅读性差,臃肿不堪,与正常流程代码结合的很紧密,所以,在JAVA中进行一系列的改良,将一系列常见的问题,用面向对象的思考方式,对其进行了描述.封装. class 处理办法 { 处理办法1() { 举例1 } 处理办法2() { 举例2 }

  • Java SpringMVC拦截器与异常处理机制详解分析

    目录 拦截器(interceptor)的作用 拦截器快速入门 案例:用户登录权限控制 拦截器方法说明 SpringMVC异常处理 异常处理的思路 异常处理两种方式 拦截器(interceptor)的作用 Spring MVC的拦截器类似于Servlet开发中的过滤器Filter,用于对处理器进行预处理和后处理. 将拦截器按一定的顺序联结成一条链,这条链称为拦截器链(Interceptor Chain).在访问被拦截的方法或字段时,拦截器链中的拦截器就会按其之前定义的顺序被调用.拦截器也是AOP思

  • SpringMVC项目异常处理机制详解

    目录 1.异常分类 2.自定义项目业务异常 3.自定义项目系统异常 4.其他异常 5.异常代码 6.异常处理器 7.异常发生 1.异常分类 通常分为三类:系统异常(SystemException),业务异常(BusinessException)和其他异常(Exception) 业务异常指由于用户的不规范操作产生的异常,如不合法的参数传入 系统异常指项目运行过程中可预计但无法避免的异常,如数据库宕机 其他异常指开发者未曾预料到的异常 2.自定义项目业务异常 public class Busines

  • C++11 中的override详解

    目录 1 公有继承 1.1 纯虚函数 (pure virtual) 1.2 普通虚函数 1.2.1 方法一 1.2.2 方法二 1.3 非虚函数 2 重写 (override) 小结: 参考资料 1 公有继承 公有继承包含两部分:一是"函数接口" (interface),二是"函数实现" (implementation) 如 Shape 类中,三个成员函数,对应三种继承方式: class Shape { public: virtual void Draw() con

  • Android中Intent机制详解及示例总结(总结篇)

    最近在进行android开发过程中,在将 Intent传递给调用的组件并完成组件的调用时遇到点困难,并且之前对Intent的学习也是一知半解,最近特意为此拿出一些时间,对Intent部分进行了系统的学习并进行了部分实践,下面将自己的学习及Intent知识进行了详细的归纳整理,希望能帮助到同样遇到相同问题的博友. 下面是Intent介绍.详解及Intent示例总结: 一.Intent介绍: Intent的中文意思是"意图,意向",在Android中提供了Intent机制来协助应用间的交互

  • node.js中watch机制详解

    几乎所有构建系统都选择使用watch机制来解决开发过程中需要反复生成构建后文件的问题,但在watch机制下,长期以来我们必须忍受修改完代码,保存完代码必须喝口茶才能刷新看看效果的问题.在这里我们尝试探讨为什么watch不是银弹,并尝试寻找一种更好的方案来解决这个问题. watch基于的事实 当一个文件修改,我们能知道其修改可能导致的文件修改,那么重新构建这些文件即可. 通常对于文件A,构建成文件B这种场景,这种对应关系是极好确定的.但现实场景下,构建过程往往不是那么简单.例如: 文件A + 文件

随机推荐