本文讲关于C++的异常的全部东西:

绝对不让异常逃离析构函数

  • 阻止exception逃离析构函数,主要是两个原因:

    1 防止在异常处理过程中的栈展开行为时,将调用terminate函数。程序将会结束,有时候事实上错误并没有那么严重。

[插入: 什么时候会调用terminate函数呢?]

[回答 : By default, the terminate handler calls abort. But this behavior can be redefined by calling set_terminate.

This function is automatically called when no catch handler can be found for a thrown exception, or for some other exceptional circumstance that makes impossible to continue the exception handling process.

//对于一个异常没有对应的catch语句块的话,就会自己主动调用terminate函数,或者是对于一些无法处理的异常的情况时。

This function is provided so that the terminate handler can be explicitly called by a program that needs to abnormally terminate, and works even if set_terminate has not been used to set a custom terminate handler (calling abort in this case).

terminate被调用的情况:

1 当发送一个异常,而且构造函数产生异常

2 当发送一个异常,或者析构函数产生异常

3 一个静态对象的构造或者析构发送一个异常

4 以atexit注冊的函数发生异常的时候

5 自己定义一个异常,可是实际上没有异常产生的时候

6 调用缺省的unexcepted()函数时候 (unexcepted函数是由于该函数抛出了没有预期的异常)

2 能够协助确保destructor完毕它应该完毕的全部的动作。

#include <bits/stdc++.h>
using namespace std; class myexception{}; class Session
{
public:
Session()
{
logCreation();
} ~Session()
{
try{ //这里的try catch块是非常重要的
logDestruction();
}catch(...){
cout << "catch exception..." << endl;
}
} private:
static void logCreation(){cout << "enter..." << endl;}
static void logDestruction() {cout << "out..." << endl;throw myexception();}
}; int main()
{
Session s;
return 0;
}

catch的子语句块 VS.函数的调用

3大类的不同:

1 exception 对象总是被复制。假设以by value的方式,甚至复制两次。

2 被抛出作为exception的对象,同意的转换操作比“被传递到函数中”的少。

3  catch子句以其出现的顺序进行匹配,也就是 “first fit”的策略, 不像函数调用的 “best fit”的策略

  • 同样点: 函数參数以及exception的传递方式都有三种: by value, by reference , by pointer,
  • 不同点: 当你调用一个函数的时候,调用完成之后控制权终于会回到该函数的调用端; 可是当你抛出一个异常的时候,控制权永远都不会回到抛出端。
  • 不同点: 函数的绑定方式,假设是传值,那么传递的是该对象的副本,假设是引用,那么直接就是绑定到该对象。可是,这对于异常是不正确的,不管你的异常的catch的參数是传值还是传引用,全部传递给该catch块的都是该对象的副本。(为什么是这样呢?由于一旦控制权离开了该函数,那么该函数的全部的局部对象都离开了生存空间,就会自己主动析构掉,那么此时传给该catch块的对象便是一个已经析构了的对象,这显然是不正确的。即使该暂时对象的生存期不只限于该函数,该结论依旧成立)。
    这里的 “exception对象总是会导致复制“的这一行为,直接导致了异常处理经常比 函数调用慢!!!
  • 当对象复制被当做一个exception的,复制行为是由对象的拷贝构造函数来运行的,这个拷贝构造函数对应于该对象的静态类型!!而不是动态类型!!比如·比如以下的代码:
class Base{
public:
Base(){cout << "ctor in Base..." << endl;}
Base(const Base &){cout << "copy ctor in Base..." << endl;}
}; class Derived : public Base {
public:
Derived(){cout << "ctor in Derived..." << endl;}
Derived(const Derived &b):Base(b){cout << "copy ctor in Derived..." << endl;}
}; void f()
{
Derived d; // print : ctor in Base ctor in Derived
Base &b = d;
cout << "throw..." << endl;
throw b; //copy ctor in Base
} void test()
{
try{
f();
cout << "never be here..." << endl;
}catch(Base) // 假设这儿是Base,那么print: copy ctor in Base,假设是Base reference,那么不输出不论什么东西
{
cout << "catch..." << endl;
}
} int main()
{
test();
return 0;
}
  • 由上面知道“exception对象是其它对象的副本”,这里就会出如今不同的catch块中怎样传递exception的问题!!!

下述的throw 和throw w分别来模拟catch块中的继续传递当前exception的操作.

//利用throw语句继续传递当前的exception,假设直接使用throw,那么传递的是最初传递来的exception的实际类型,(test1中的throw)
//假设是使用throw + catch形參的方式,那么传递的是静态类型。 class Base{
public:
Base(){cout << "ctor in Base..." << endl;}
Base(const Base &){cout << "copy ctor in Base..." << endl;}
}; class Derived : public Base {
public:
Derived(){cout << "ctor in Derived..." << endl;}
Derived(const Derived &b):Base(b) {cout << "copy ctor in Derived..." << endl;}
}; void f1()
{
try{
Derived d; // ctor in Base , ctor in Derived
Base &b = d;
cout << "throw..." << endl;
//此处throw的是Derived对象
throw d; //copy ctor in Base , copy ctor in Derived,这里看的是静态类型的!!!
}catch(Base &w) //Base&是能够捕捉到上述的Derived异常的,不管此处是Base还是Base&,都能捕获到Derived类型的异常。
// 假设此处是Derived,那么上述的throw假设throw的是b,那么这里不能捕获到。与一般的函数调用的參数绑定方式是一样的。
{
cout << "throw current exception 1, using throw only..." << endl;
throw ; //假设直接使用throw,即使上面的catch是将一个Derived对象绑定到一个Base对象,这里传递的依旧是最初的Derived对象。
}
} void test1()
{
try{
f1();
cout << "never be here..." << endl;
}catch(Derived) //copy ctor in Base, copy ctor in Derived
//}catch(Base) // copy ctor in Base .
{
cout << "catch..." << endl;
}
} void f2()
{
try{
Derived d; // print : ctor in Base ctor in Derived
Base &b = d;
cout << "throw in f2..." << endl;
throw b; //copy ctor in Base //这里的throw传递的则是静态类型。
}catch(Base &w)
{
cout << "throw current exception 2 " << endl;
throw w; //copy ctor in Base
}
} void test2()
{
try{
f2();
cout << "never be here in test2..." << endl;
}catch(Base &) // 假设这儿是Base,那么print: copy ctor in Base,假设是Base reference,那么不输出不论什么东西
{
cout << "catch in test2..." << endl;
}
}
int main()
{
cout << "1 ; "<< endl;
test1();
cout << "2 : " << endl;
test2();
return 0;
}

  • 不同点: 一个被抛出的对象(依据上述显然是个暂时对象)能够以 by reference的方式捕捉,不须要以一个by reference to const的方式捕捉,可是函数调用的过程中将一个暂时对象传递给一个non-const-reference參数是不同意的。可是对exception合法。

    将一个暂时对象传递给一个non-const-reference參数为什么是不同意的。 ]

    考虑以下的样例:

void f(const string &s)
{
cout << s << endl;
cout << "const-reference..." << endl;
return;
} void f2(string &s)
{
cout << "non-const reference..." << endl;
return;
} int main(void)
{
char buff[20] = "1234";
//f和f2函数须要的是一个string的对象,可是传递的都是buff字符数组,那么这里就会有一个隐式类型转换,生成一个暂时的string对象。
f(buff); //调用成功。由于是const,不须要改变什么。
f2(buff); //调用失败,原因:f2须要的是string的non-const reference,也就意味着f2函数可能会改动字符串s,而此时传递给
//f2的是一个string的暂时对象,在函数退出时,该实參没有改变,全部的操作均施加于暂时形參s上,显然这并非程序猿的意图,我们传递一个
//non-const reference给f2,应该就是希望改动buff这个实參的。
return 0;
}

  • 不同点: 假设以by value的方式传递一个函数自变量,那么便是对被传递对象做一个副本,此副本存储于相应的函数參数中; 假设以 by value的方式传递exception,会怎么样呢? 比如 :

    catch (Base b) 。。。 //预期得付出 “被抛出物” 的 ”两个副本“的构造代价,一个用于 ”不论什么exception都会产生的暂时对象的“身上,另一个是 ”将暂时对象拷贝到b”。
  • 假设以by reference的方式的话,那么函数不须要不论什么额外动作,可是 exception 还是会有一个 “被抛出物”的副本的构造的代价。
  • 假设以by pointer的方式来传递exception的话,那么必须注意的是千万不要跑出一个指向局部对象的指针。这也是 “义务性复制操作”必须考虑的情况。

  • 不同点:throw子句与catch參数的匹配!!

    exception与catch子句的匹配过程只不过两种转换: 1 , 继承架构中的类转换。 (一个针对Base class exception编写的catch子句,能够捕捉到一个derived class的exception) ,注意: 这个转换规则适用于以by value, by reference,或者是by pointer的三种方式。 2、 从一个“有型指针” 转换为一个 “无型指针”,比如一个针对于: const void *设计的catch子句,能够捕捉随意的指针类型的exception。

  • 不同点: catch子句总是依出现的顺序进行匹配。也就是说,针对Derived class exception的catch子句一定要放在针对Base class exception的catch子句的前面。

Catch exception By reference

理由:

+ 假设是by pointer的话,那么须要的是一个超出该作用域都不会被销毁的对象,那么此时global和static是能够帮忙的,可是程序猿们可能会忘记这些;假设就是在throw的时候才暂时的构造一个exception对象,比方: throw new exception,那么catch子句是否要删除这个对象呢?诚然,假设是global或者static,那么显然不须要删除,可是假设是new的,显然就须要删除。

+ 假设是by value的话,就会出现对象分割的问题,当把一个Derived exception的对象给一个Base exception的时候,那么会出现对象分割的问题,这个时候假设你在catch块中调用了虚函数,那么该函数的版本号就仅仅是基类的版本号。显然这不是我们要的。

使用Catch exception by reference,便能够避免这些问题。

C++ 之 exception的更多相关文章

  1. [C#] C# 知识回顾 - 你真的懂异常(Exception)吗?

    你真的懂异常(Exception)吗? 目录 异常介绍 异常的特点 怎样使用异常 处理异常的 try-catch-finally 捕获异常的 Catch 块 释放资源的 Finally 块 一.异常介 ...

  2. 浅谈java异常[Exception]

    学习Java的同学注意了!!! 学习过程中遇到什么问题或者想获取学习资源的话,欢迎加入Java学习交流群,群号码:589809992 我们一起学Java! 一. 异常的定义 在<java编程思想 ...

  3. Exception in thread "main" java.lang.NoClassDefFoundError: org/apache/commons/logging/LogFactory

    学习架构探险,从零开始写Java Web框架时,在学习到springAOP时遇到一个异常: "C:\Program Files\Java\jdk1.7.0_40\bin\java" ...

  4. Exception in thread "main" java.lang.NoSuchMethodError: org.objectweb.asm.ClassWriter.<init>(I)V

    在学习CGlib动态代理时,遇到如下错误: Exception in thread "main" java.lang.NoSuchMethodError: org.objectwe ...

  5. Atitit 解决Unhandled event loop exception错误的办法

    Atitit 解决Unhandled event loop exception错误的办法 查看workspace/.metadata/.log org.eclipse.swt.SWTError: No ...

  6. Java中的Checked Exception——美丽世界中潜藏的恶魔?

    在使用Java编写应用的时候,我们常常需要通过第三方类库来帮助我们完成所需要的功能.有时候这些类库所提供的很多API都通过throws声明了它们所可能抛出的异常.但是在查看这些API的文档时,我们却没 ...

  7. Error on line -1 of document : Premature end of file. Nested exception: Premature end of file.

    启动tomcat, 出现, ( 之前都是好好的... ) [lk ] ERROR [08-12 15:10:02] [main] org.springframework.web.context.Con ...

  8. Android开发学习之路-关于Exception

    Exception在Java中是表示异常的一个类.它是Throwable的子类. 而Exception的子类RuntimeException是一个特殊的异常类,在代码中不需要对此类进行throw,而是 ...

  9. 严重: Exception sending context initialized event to listener instance of class

    问题描述:Exception sending context initialized event to listener instance of class org.springframework.w ...

  10. python之最强王者(11)——异常(exception)

    1.Python 异常处理 python提供了两个非常重要的功能来处理python程序在运行中出现的异常和错误.你可以使用该功能来调试python程序. 异常处理: 本站Python教程会具体介绍. ...

随机推荐

  1. quartz.net持久化和集群

    首先你应该使用的是持久化的quartz,所有定时任务的情况都是保存在数据库表总的,每次启动时,scheduler容器都是按照qrtz_triggers等表内存储的信息来执行定时任务(主要包括cron表 ...

  2. C++实现堆排序

    堆排序是合并排序和插入排序排序方法共同的长处.它的时间复杂度O(nlgn),这也是一个地方排序算法:在任何时候,外阵中拥有唯一不变的输入数组存储的元素.引进第一家引进什么样的堆堆. 1.建堆:堆数据结 ...

  3. SpringMVC+Spring+Hibernate的小样例

    Strusts2+Spring+Hibernate尽管是主流的WEB开发框架,可是SpringMVC有越来越多的人使用了.确实也很好用.用得爽! 这里实现了一个SpringMVC+Spring+Hib ...

  4. React Native是一套使用 React 构建 Native app 的编程框架

    React Native是一套使用 React 构建 Native app 的编程框架 React Native at first sight what is React Native? 跟据官方的描 ...

  5. 文章13称号 Add Two Numbers

    You are given two linked lists representing two non-negative numbers. The digits are stored in rever ...

  6. 好的安排小明(南阳19)(DFS)

    擅长排列的小明 时间限制:1000 ms  |  内存限制:65535 KB 难度:4 描写叙述 小明十分聪明,并且十分擅长排列计算.比方给小明一个数字5,他能立马给出1-5按字典序的全排列,假设你想 ...

  7. 《TCP/IP作品详细解释2:达到》注意事项--IP地址

    1.接口和地址 如下面的图全部本文中讨论的接口和地址的结构看一个示例配置: 上图中显示了我们三个接口样例:以太网接口,SLIP接口和环回接口. 它们都有一个链路层地址作为地址列表中的第一个结点. 显示 ...

  8. 利用Ring Buffer在SQL Server 2008中进行连接故障排除

    原文:利用Ring Buffer在SQL Server 2008中进行连接故障排除 出自:http://blogs.msdn.com/b/apgcdsd/archive/2011/11/21/ring ...

  9. C语言,如何检查文件是否存在和权限的信息

    按功能access,头文件io.h(linux通过使用unistd.h    int   access(const   char   *filename,   int   amode); amode參 ...

  10. Canvas rontate(旋转) 使用误区

    context.setTransform(1,0,0,1,0,0);//重置转换为初始化状态 var angleInRadians = 45 * Math.PI / 180;var width = 4 ...