c++的历史-异常

1.异常出现的目的

在c++语言的设计和演化中,Bjarne Stroustrup说过异常的设计假定如下情况:

  1. 基本上是为了处理错误

  2. 与函数定义相比,异常处理是很少的

  3. 与函数调用相比,异常出现的频率较少

  4. 异常仅仅是语言层次上的概念

同时:

  1. 异常不是为了作为另外一种返回机制,而是一种容错机制

  2. 不是想把函数都转变成一个容错的试题,而是想作为一种机制,提供给子系统容错能力,即使各个函数在写法上没有关心全局的错误处理

  3. 并不是想将设计者都约束到一个正确的错误处理概念上,而是希望语言更有表达能力

所以说异常实际上最早出现我想更多是c++对c一种容错机制的补充,而不是对于返回值的改进,现在很多书上都提倡用异常机制替代返回值,宣称异常机制有很多优点,也有很多人鄙视异常机制,比如zero
mq的作者就曾经说过,异常机制无法做到错误处理和引发错误的地方的耦合,我想他们都理解错了异常机制出现的目的

2.异常与返回值判断错误的区别(我自己的理解)

作为一种容错机制,异常并未想要取代返回值检查错误的改进,其中一个最显著的区别就是返回值即使你不是处理,并不会导致程序的崩溃,而异常会,即返回值的默认语义就是:

  1. 我这个函数会发生错误

  2. 即使我发生错误,你可以无视我的错误

而异常机制的语义就是:

  1. 如果我发生错误,你必须处理我这个错误,否则程序就会崩溃

异常机制强制你去处理一些事情,你必须明确的知道我这个函数中,我发生错误,并且我处理了他,

而返回值则没有这种强制性

所以我建议大家在编写c++代码的时候,要考虑下,如果我这个函数出现了错误,我是否允许一些程序继续往下执行??再来决定是否使用异常还是返回值,以上仅仅是我的个人建议,而不是bjarne
stroustrup的建议

3.为什么c++不让用户知道函数会引发什么异常

既然异常机制的语义是如果我发生错误,你必须处理我这个错误,但是在函数的声明上,我们很难看出来这个函数是否需要捕获异常,因此,最早c++的设计团队有这样的想法,就是在函数的声明加上异常抛出的语义

void function(int args) throw e1,e2

这样用户在看到这个函数声明的时候就知道这个函数会抛出什么异常,其实我觉得这样挺方便的,现在我无法知道内部程序会抛什么异常一直是我所困扰的,如果这样,我们就可以在编译的时候知道我们的程序有多少未捕获的异常

假设c++真的实现了异常的静态检查,我们在编译的时候就可以直接检测到代码中的错误,会发生什么问题??

?假设我们有三个函数,其中存在不同的三个模块,

?分别为function0调用function1,function1调用function2,此时function2抛出异常e1,需要function0处理,function1和function0的模块为了不编译错误,function1必须捕获函数并且把他重新throw,而function0必须处理这个异常

后面,function2增加了异常抛出e2,此时我们为了通过编译,我们需要重新修改function1的代码,并且重新编译function1,这个修改也会影响到function0,于是我们也必须去修改function0,这样,将会导致多余的代码修改以及重编译,这个不是c++的设计团队所希望看到的

所以后来bjarne
stroustrup认为这样的静态检查并不是c++所希望的,他更认为这种检查将由另外一种工具来检查更好

我在想这种语法作为一种提示语法也并非不可,只是作为提示用,编译器不会对此进行保证,不过回头想想,这跟注释有什么区别??

4:异常的核心就是资源管理

在异常里面还有一个比较头疼的就是资源管理,bjarne
stroustrup也明确指出异常的设计核心实际上是资源的管理,原因在于如果一个程序打开了一个文件,程序在运行过程中抛出了异常,而文件关闭的代码则在程序抛出异常代码的后面,那么则文件无法正常关闭

于是这个时候,RAII就诞生了,我看到这里也感到有点诧异,RAII居然是因为异常诞生的

于是,不管我们是否进行文件关闭操作,只要该函数析构掉,我们的文件总能正常关闭

bjarne
stroustrup也提出异常处理给构造函数提供了一种报告出错的直接方法,如果没有异常函数,那么我们将只能迂回地去检测这个函数是否构造完成,与此相反,scotter
meyers还是hutter(忘记是谁了,反正是一位c++大神)建议,就是不要在构造函数中抛出异常,因为如果在构造函数里面抛出异常,那个申请的资源可能会泄漏,比如

x = new X()

如果在构造x的过程中抛出异常,那么你就无法删除new
X申请的资源,这个也成了很多人嘲笑c++异常的例子

于是乎,很多程序转而另外定义一个init来供外部调用,这个也是我经常在网上看到的建议,于是你经常能看到如下的代码:

x = new X()

try

{

x->init();

}

catch(...)

{

}

但是bjarne
stroustrup对对这种做法不赞成(http://www.cise.ufl.edu/~manuel/stroustrup/ex.pdf),

Having a separate init() function is an opportunity
to
– Forget to call init()
– Call init()
twice
– Forget to test that init() succeeded

Forget that init() might throw an exception
– Use an object
before calling init()

对此,scotter meyers在他的more effective c++的item
10有给出建议,总的来说,还是要遵守不要在构造函数里面对外抛出异常,而是在构造函数内处理异常,并且在构造函数内利用RAII来达到自动管理资源的目的,当然后一条不是必须的,只要你能保证你能够正常释放资源即可,利用RAII只是为了方便而已

 

5.为什么没有办法从发生异常的地方继续执行

我想这个是很多人一直想要的功能,当时这个问题c++讨论组也讨论了很久,最后从以往的设计角度上来看,这种所谓的唤醒机制都是不靠谱的,因此没有加入,有一个os最早就是朝着这个方向设计的,后来几乎把这部分唤醒功能全部都去掉了,我想估计c++委员会也是基于这样的考虑才不加入这个功能

 

上面的就差不多是c++异常机制设计的一些思路了,当然这里面参杂了我的一些看法,其实从这里面来看,很多人不喜欢c++是因为他不了解c++一些特性出现的原因,可能他不知道这个在c++里面实际上是错误的用法,只是c++没去禁止你使用而已

最近看<<C++的历史和演化>>感觉还是挺有意思的,了解c++的历史就知道我们在过去有多少错误的c++使用例子了

?

同时推荐下bjarne
stroustrup的文章  Standard-Library Exception Safety

http://www.cise.ufl.edu/~manuel/stroustrup/ex.pdf

时间: 2024-10-11 21:23:24

c++的历史-异常的相关文章

GBase入库流程详细笔记

1.Gbase入库的准备工作:1).输入表和输出表的字段必须对齐(对齐指字段的数量相等.字段的名称一致)2).gbase表里和spark表里都必须有分区字段(入库的脚本是按天或按月入库的),否则会报错或者任务显示成功,实质上并没有成功入库. 2.Gbase入库大体流程:1) 第一步:算法(vmax_preProcessForGBbase_0.0.1.jar)从spark表中清洗到hdfs路径:/metadata/hdfs/load2gbase; gbasefile{ classname=com.

ichat在线客服jQuery插件(可能是历史上最灵活的)

ichat是一款开源免费在线客服jQuery插件,通过该插件,您可以自由的定制属于自己的在线客服代码. ichat充分吸收传统在线客服插件的优点,并加上自身的独特设计,使得ichat可定制性异常强大. ichat追求简单实用,走小清新路线,以便能够适应大多数网站风格. ichat几乎全部由配置生成,使用简便,不需要写html,但需要写js,因为配置是json对象. ichat体积小巧,代码高效,兼容性良好,采用jQuery插件封装,与原有系统零冲突. 效果预览图: 更多内容参见: ichat项目

java历史简介

java历史简介 Java是一种计算机编程语言,拥有跨平台.面向对象.泛型编程的特性,广泛应用于企业级Web应用开发和移动应用开发. 1991 年Sun公司的James Gosling等人开始开发名称为 Oak 的语言.希望用于控制嵌入在有线电视交换盒.PDA等的微处理器 1994年将Oak语言更名为Java 1998年JDK1.2时,更名为Java 2 Platform 分为标准版J2SE,企业版J2EE,微型版J2ME Java 既安全.可移植,又可跨平台,而且人们发现它能够解决Intern

FileOutputStream&amp;FileInputStream&amp;异常的使用

我们总觉得历史是极其遥远的东西,与我们并无关联,又觉得历史隐藏在图书馆的旧书之中. 然而,我们每个人都有真真切切的历史.那便是每日的历史.今天自己做了些什么,又是怎么做的,都会成为你历史中的一页. 是畏缩不前.碌碌无为,还是勇猛挑战,花心思比昨天做的更好.每种态度,都会写就属于你的历史. --尼采 <快乐的知识> 以下是今天的练习,这些是自己在看着官方说明文档写出来的练习: 1 package Zhang; 2 3 import java.io.File; 4 5 import java.io

记录一次追查server死机&amp; 登录异常情况

linux 服务器死机了,于是追查原因. 查看boot.log [email protected]:/var/log$ less boot.log 看不出异常 显示开机信息 [email protected]:/var/log$ less dmesg 区别;(未明) /var/log/boot.log --- System boot log /var/log/dmesg --- print or control the kernel ring buffer /var/log/messages看看

Java的发展历史

Java自1995诞生,至今已经20多年的历史. Java的名字的来源:Java是印度尼西亚爪哇岛的英文名称,因盛产咖啡而闻名.Java语言中的许多库类名称,多与咖啡有关,如JavaBeans(咖啡豆).NetBeans(网络豆)以及ObjectBeans (对象豆)等等.SUN和JAVA的标识也正是一杯正冒着热气的咖啡.据James Gosling回忆,最初这个为TV机顶盒所设计的语言在Sun内部一直称为Green项目.我们的新语言需要一个名字.Gosling注意到自己办公室外一棵茂密的橡树O

js 函数声明方式以及javascript的历史

1.function  xx(){} 2.匿名方式   window.onload=function(){dslfjdslfkjdslf}; 3.动态方式  var demo=new Function('x','y','var y=x+y;return y;'); var sum=demo(4,55); alert(sum); 一.JavaScript的历史 a) 1992年Nombas开发出C-minus-minus(C--)的嵌入式脚本语言(最初绑定在CEnvi软件中).后将其改名Scrip

进程—异常控制流之陷阱篇

一.Exceptions(异常) and System Call(系统调用) 1.1 陷阱 陷阱是有意为之的异常,是处理器执行程序的一条指令的结果.陷阱最重要的用途是提供用户程序和内核之间一个像普通过程调用似的接口,名曰:系统调用.用户程序经常需要向内核请求服务,比如读一个文件(read) .创建一个新的进程(fork) .加载一个新的程序(execv),或者终止当前进程(exit) .为了允许对这些内核服务的受控的访问,处理器提供了一条特殊的 "syscall n" 指令,当用户程序

中国历史上那些奇女子

吕雉汉王朝创始人汉高祖刘邦的正配夫人.是中国历史上三大女性统治者(吕后,武则天,慈禧太后)的第一个. 她为人有谋略而性残忍,在刘邦翦除异姓诸侯王的过程中起了很大作用. 她虽为女流,但不乏政治家的风度,不管是帮助刘邦打天下还是把持政事,每每从容不迫,识破奸人计谋,化险为夷.吕后当政内,创自刘邦的休养生息的黄老政治进一步得到推行.刘邦临终前,吕后问刘邦身后的安排.她问萧何相国后谁可继任,刘邦嘱曹参可继任;曹参后有王陵,陈平,但不能独任;周勃忠诚老实,文化不高,刘家天下如有危机,安刘氏天下的必是周勃,