{讨论}{技术}{C++} 关于错误处理

35 views
Skip to first unread message

kelviN

unread,
Mar 18, 2010, 9:32:39 AM3/18/10
to TopLanguage
虽然是老话题了,但是在项目中实践起来还是觉得还是很难做好。
就2种方式:错误码和异常,错误码没什么好说的,一般书上都建议用异常,这里也主要讨论异常。(就我现在目前的C++项目来说,都是很简单的bool值
返回,来判断一个函数是否成功,连错误码都没有,更不用谈什么异常,当然了项目也比较老。)
基本的什么异常安全性保证,异常安全公理就不说了,书上都有。

这里主要想讨论几个难点,主要是通过举例子问一些我比较疑惑的问题,希望能讨论出一个简单的应用规则或方法:
1.怎么区分错误和非错误
用书上的话说就是:“违反约定就是错误:函数是一个工作单元。因此,失败应该视为错误,或根据其对函数的影响而定。在函数f中,当且仅当失败违反了f的
一个前条件,或者阻碍了f满足其调用代码的任何前条件,实现f自己的任何后条件或者重新建立f有责任维持的不变式时,失败才是一个错误。特别排除内部的
程序设计错误。”
例1:Widget* createWidget();这样一个工厂函数,构造对象不成功算不算一个错误?如果算,应该返回NULL,但是否还应该抛出
个异常?
例2:类里面一个set函数,设一个值的时候如果失败,是否当做一个错误抛出异常?
例3:防御式编程检查参数时,发现参数无效都抛异常?

2.什么时候捕获
例4:STL会抛异常,比如容器的at()越界时,一般都是在调用点立即捕获?
例5:new也会抛异常(虽然这个我基本没怎么碰到过),但是是否需要在new的时候在调用点捕获?
例6:构造函数失败抛异常,在建对象的地方就准备捕获?还是改用init()代替构造函数?

3.什么时候用异常
例7:有一点说的是:“能在调用点解决的,就可以不用异常(而改用返回码)”。但我在设计一个函数接口的时候,我如何知道这个错误被调用者在哪里处
理?
例8:若果发现错误码是层层传递的,这种情况是否应该改用异常?

4.其他语言怎么做的
其他语言我不是很熟悉,请有经验的同学谈谈。
像java,python貌似已经很少看到用错误码处理错误了,GC帮了大忙?
C++应该提倡防御式编程,erlang中不建议防御式编程?


最后说下自己的看法:异常处理有个“度”的把握,就像说是“根据其对函数的影响而定”,像set/get/create之类的函数我想很少有人会在里面
抛出异常;而真正你发现需要用错误码(大多数人一般都会先想到返回一个false吧)一层层往上传递的时候,具体多少层根据设计也会不同(“度”的把
握),或许用异常是个正确的选择。当然,也这需要很多的经验积累来增强自己的判断和把握。

问题比较杂,大家随意发表自己的看法。

Shuo Chen

unread,
Mar 18, 2010, 10:30:06 AM3/18/10
to TopLanguage
简言之,不要在 C++ 里用异常,这个特性与语言的其他部分格格不入。

出错了,要么直接打出 stace trace,然后 core dump;要么用 error code 返回上层,这种情况下其实不算错误,是程序
逻辑的一部分。
抛异常的话,调用栈都展开了,上下文也丢了,不利于事后排查。
比如你举的例子:
1. 返回 NULL 就好,如果构造可能不成功的话。
2. setter 函数如果不成功,说明程序出大问题了,直接 core dump 吧。除非这个 set 函数返回 bool,表明成功与否。
3. 参数检查本来就是逻辑的一部分,不算意外,用返回值,还便于 switch case 归类统计。
4. 如果你用 at(),说明你知道可能越界,那为什么不自己判断好,再调用 operator[] ?
5. bad:alloc 捕获了你又能干啥?虚拟内存都不够了,干脆 core dump 吧,这样还能从 core 文件验尸,看看究竟哪些对象占
了内存。
x. 对于网络库,连接意外断开不是异常,网络编程就是要考虑各种情况。

Yongwei Wu

unread,
Mar 18, 2010, 10:57:53 AM3/18/10
to pon...@googlegroups.com
嘿嘿,干吗不用?我在公司一向倡导使用异常。

不使用异常的最大问题在于,你得使用返回值表示成功与否,语言的表达能力就
大大下降。能够让函数调用返回有意义的数值对语言的表达能力是非常重要的。

拿一个我在C++技术大会上讲过的例子。VC 2005开始,fstream的文件不能传入
非ASCII字符,除非你调了合适的setlocale。在库代码中,很难保证这一点。我
使用的技巧是写一个封装类。

class AnsiToWideString {
public:
explicit AnsiToWideString(const char* ansi_string);
~AnsiToWideString();
operator const wchar_t*() const;

private:
wchar_t* wide_string_;
};

然后:

#if defined(_MSC_VER) && _MSC_VER >= 1400
typedef AnsiToWideString FileStreamName;
#else
inline const char* FileStreamName(const char* filename)
{
return filename;
}
#endif

使用时,只需把原先的

ifstream ifs(filename);

改成

ifstream ifs(FileStreamName(filename));

即可。

不用异常,要么无法处理字符转换出错或内存不足的情况,要么调用的代码没办
法这样精简。

另外,至少在Windows上我碰到过内存很多,但分配失败的情况。一般是临时性
的。

自己系统的软件可以core dump,卖给别人的产品永远不可以。

2010/3/18 Shuo Chen <gian...@gmail.com>:

--
Wu Yongwei
URL: http://wyw.dcweb.cn/

Shuo Chen

unread,
Mar 18, 2010, 11:07:01 AM3/18/10
to TopLanguage
契约思想的一个反面案例 C++ IOStream
http://blog.csdn.net/myan/archive/2003/01/17/1922.aspx

> 2010/3/18 Shuo Chen <giantc...@gmail.com>:

jinhu wang

unread,
Mar 18, 2010, 12:05:54 PM3/18/10
to pon...@googlegroups.com
不用异常是好事,用了异常是灾难。
为何你会说返回值会令语言的表达能力下降呢?

jinhu wang

unread,
Mar 18, 2010, 12:09:09 PM3/18/10
to pon...@googlegroups.com
自己系统的软件可以core dump,卖给别人的产品永远不可以。
所以在卖给别人前要尽力排查各种可能的core dump
在 2010年3月18日 下午10:57,Yongwei Wu <wuyo...@gmail.com>写道:

kelviN

unread,
Mar 18, 2010, 12:13:00 PM3/18/10
to TopLanguage
如果不用异常,我就觉得没什么好讨论的了。。
C++ coding standard #72也说了优先使用异常报告错误。
再举个书上的例子(#70):一个这种函数检查参数并需要抛出3种异常?
如果要设计一个File类,在同一个函数File::Write(const char* buffer, size_t size)中,要求
buffer非空,并以写方式打开文件,那么可能会决定做以下工作:
- 如果buffer为NULL:报告一个有关违反前条件的错误。
- 如果File只读:报告一个有关违反前条件的错误。
- 如果写入不成功:报告一个有关违反后条件的错误,因为函数无法实现承诺要完成的操作。

Shuo Chen

unread,
Mar 18, 2010, 12:46:58 PM3/18/10
to TopLanguage
对于 File::Write:
buffer 应该用 assert,因为这是程序错误
File 只读的话,也是 assert,同样是程序逻辑错误,为什么程序要往一个 read-only 的文件里写东西,这个在创建对象的时候就该失
败。
写入不成功是程序能预料到的,不是异常,原因很多:
1. 磁盘满了,配额用完了,程序无计可施
2. 磁盘故障,errno == EIO,程序照样无计可施,抛异常能怎么样?直接报个错,死掉拉倒。
3. 系统的 write(2) 本来就不保证全部写入,它通过返回值告诉你写了几个字节。File class 凭什么能承诺一定写得进去?它要么
用 while (!finish) { } 来不断尝试,要么直接告诉用户写了几个字节。

机械唯物主义 : linjunhalida

unread,
Mar 18, 2010, 8:02:28 PM3/18/10
to pon...@googlegroups.com
我觉得按照错误的类型来判断:
逻辑错误需要程序来回应,这个时候需要处理。
操作错误,可以根据具体的业务状况来判断处理方法,应该通知调用者。
致命错误,比如内存不够之类的,让程序挂掉,出个报错记录。
其他未知错误,能忽略就忽略,补上log。

2010/3/19 Shuo Chen <gian...@gmail.com>

sagasw

unread,
Mar 18, 2010, 8:47:47 PM3/18/10
to pon...@googlegroups.com
不用异常的一个好处是挡住了那些用异常做正常逻辑的人,
手里拿着锤子,一定要什么地方都打打。

返回值加上memory dump,商业公司为何不能使用?歧视商业公司么,哈哈

------------------------------------
C++, Lua, living in Dalian
http://sunxiunan.com/
http://twitter.com/sagasw
------------------------------------


2010/3/19 kelviN <kowo...@gmail.com>

Shuo Chen

unread,
Mar 18, 2010, 9:02:29 PM3/18/10
to TopLanguage
问题在于,在一个多人团队里,每个人对“错误的类型”的观点很可能不一致。

如果用异常,对同一个错误,有人觉得该抛,有人觉得不该抛。
我的函数如果往外抛,我怎么知道接收方有没有打算接住这个异常呢?
如果我调用一个函数,我怎么知道它和它调用的子程序会不会抛出我没有想到的异常?(我总不能来一次广度优先遍历,把代码全查一遍吧)
如果调用我的程序和我调用的程序都不是我写的,那么大家如何形成共识,不出现漏抛和漏接的情况?
如果要靠文档来约定,为什么不把文档做到代码里,用返回值(一个 enum)作为文档呢?

对于一个人的项目,抛不抛都行,左右半脑直接沟通好即可。
对于多人项目,乱抛异常跟随处大小便没啥区别。

On 3月19日, 上午8时02分, 机械唯物主义 : linjunhalida <linjunhal...@gmail.com>
wrote:


> 我觉得按照错误的类型来判断:
> 逻辑错误需要程序来回应,这个时候需要处理。
> 操作错误,可以根据具体的业务状况来判断处理方法,应该通知调用者。
> 致命错误,比如内存不够之类的,让程序挂掉,出个报错记录。
> 其他未知错误,能忽略就忽略,补上log。
>

> 2010/3/19 Shuo Chen <giantc...@gmail.com>

haozes

unread,
Mar 18, 2010, 9:24:19 PM3/18/10
to pongba
楼主所说的可以分为两个问题:

1.C++中到底提倡不提倡使用异常
2.如何正确使用异常.

第2个问题,可以暂不讨论.现在就第一个问题,我认为在C++的世界里都很难找到定论.
我用的比较多的是.NET之类的,或者比较新一点的语言都有介绍如何正确使用异常.到底何时抛异常,也是没有那么绝对的问题.可以参照那些.

C++应该提倡防御式编程,erlang中不建议防御式编程?
防御性编程,应当是在所有语言中都应提倡的.

最近我也在迷惑这个问题.有的C++书上说提倡使用异常,有的说坚决不用.如<Debugging Applications for Microsoft .NET and Microsoft Windows>作者就说坚决不要在C++中用异常.这两种观点导致了C++世界已有的代码产生了两种风格.而作为一个我这样的C++新手,似乎选哪一种方法,都郁闷.

就个人而言,我倾向使用异常,像C#一类的抛出异常,堆栈信息也可以看到.也许C++里就没有那么方便.促进C++里不赞成使用异常一方倾向.性能上我认为不足为虑,又不是不停的触发异常.

搭车问个问题:
在C++编写的应用程序中,如果捕获未处理的异常?.UnhandledException 的处理可能要框架的支持.


2010/3/19 Shuo Chen <gian...@gmail.com>
To unsubscribe from this group, send email to pongba+unsubscribegooglegroups.com or reply to this email with the words "REMOVE ME" as the subject.

sagasw

unread,
Mar 18, 2010, 9:38:04 PM3/18/10
to pon...@googlegroups.com
在C++编写的应用程序中,如果捕获未处理的异常?
如果你写的是Windows程序,可以到我博客里搜索关键字,写过几篇。这个未处理异常其实不是C++的机制,而是SEH。


------------------------------------
C++, Lua, living in Dalian
http://sunxiunan.com/
http://twitter.com/sagasw
------------------------------------


2010/3/19 haozes <hao...@gmail.com>

superzxt

unread,
Mar 18, 2010, 9:49:09 PM3/18/10
to pon...@googlegroups.com
一个典型的例子:Google C++ Style Guide,可以参考一下

Exceptions

We do not use C++ exceptions.
link

Pros:

  • Exceptions allow higher levels of an application to decide how to handle "can't happen" failures in deeply nested functions, without the obscuring and error-prone bookkeeping of error codes.
  • Exceptions are used by most other modern languages. Using them in C++ would make it more consistent with Python, Java, and the C++ that others are familiar with.
  • Some third-party C++ libraries use exceptions, and turning them off internally makes it harder to integrate with those libraries.
  • Exceptions are the only way for a constructor to fail. We can simulate this with a factory function or an Init() method, but these require heap allocation or a new "invalid" state, respectively.
  • Exceptions are really handy in testing frameworks.

Cons:

  • When you add a throw statement to an existing function, you must examine all of its transitive callers. Either they must make at least the basic exception safety guarantee, or they must never catch the exception and be happy with the program terminating as a result. For instance, if f() calls g() calls h(), and h throws an exception that f catches, g has to be careful or it may not clean up properly.
  • More generally, exceptions make the control flow of programs difficult to evaluate by looking at code: functions may return in places you don't expect. This results maintainability and debugging difficulties. You can minimize this cost via some rules on how and where exceptions can be used, but at the cost of more that a developer needs to know and understand.
  • Exception safety requires both RAII and different coding practices. Lots of supporting machinery is needed to make writing correct exception-safe code easy. Further, to avoid requiring readers to understand the entire call graph, exception-safe code must isolate logic that writes to persistent state into a "commit" phase. This will have both benefits and costs (perhaps where you're forced to obfuscate code to isolate the commit). Allowing exceptions would force us to always pay those costs even when they're not worth it.
  • Turning on exceptions adds data to each binary produced, increasing compile time (probably slightly) and possibly increasing address space pressure.
  • The availability of exceptions may encourage developers to throw them when they are not appropriate or recover from them when it's not safe to do so. For example, invalid user input should not cause exceptions to be thrown. We would need to make the style guide even longer to document these restrictions!

Decision:

On their face, the benefits of using exceptions outweigh the costs, especially in new projects. However, for existing code, the introduction of exceptions has implications on all dependent code. If exceptions can be propagated beyond a new project, it also becomes problematic to integrate the new project into existing exception-free code. Because most existing C++ code at Google is not prepared to deal with exceptions, it is comparatively difficult to adopt new code that generates exceptions.

Given that Google's existing code is not exception-tolerant, the costs of using exceptions are somewhat greater than the costs in in a new project. The conversion process would be slow and error-prone. We don't believe that the available alternatives to exceptions, such as error codes and assertions, introduce a significant burden.

Our advice against using exceptions is not predicated on philosophical or moral grounds, but practical ones. Because we'd like to use our open-source projects at Google and it's difficult to do so if those projects use exceptions, we need to advise against exceptions in Google open-source projects as well. Things would probably be different if we had to do it all over again from scratch.

There is an exception to this rule (no pun intended) for Windows code.

Ian Yang

unread,
Mar 18, 2010, 10:17:27 PM3/18/10
to pon...@googlegroups.com
还是程序员自身的问题,用异常最怕不异常安全的代码和顺意吞掉异常不做任何处理。同样用错误代码没办法强制对返回值进行检查。

2010/3/19 superzxt <supe...@gmail.com>

To unsubscribe from this group, send email to pongba+unsubscribegooglegroups.com or reply to this email with the words "REMOVE ME" as the subject.



--
Sincerely, Ian Yang


kelviN

unread,
Mar 18, 2010, 10:27:26 PM3/18/10
to TopLanguage
.NET, Java之类的语言为什么就用异常来处理错误呢?或者说它们那套规则为什么不能应用在C++里面,是由语言提供了某些特殊的功能或本身的机
制提供了某些保证?
顺便提下erlang的故障即停的错误处理哲学:
- 速错
- 不成功,便成仁
- 任它崩溃
- 杜绝防御式编程
当然erlang和C++差别太大,不太好比较。
也许这些语言中所认为的错误已经是非常严重的那种,或者说我们通常碰到的都不是错误,都是逻辑判断而已,遇到错误就应该抛异常或干脆当机core
dump。

On Mar 19, 9:24 am, haozes <hao...@gmail.com> wrote:
> 楼主所说的可以分为两个问题:
>
> 1.C++中到底提倡不提倡使用异常
> 2.如何正确使用异常.
>
> 第2个问题,可以暂不讨论.现在就第一个问题,我认为在C++的世界里都很难找到定论.
> 我用的比较多的是.NET之类的,或者比较新一点的语言都有介绍如何正确使用异常.到底何时抛异常,也是没有那么绝对的问题.可以参照那些.
>

> *C++应该提倡防御式编程,erlang中不建议防御式编程?*


> 防御性编程,应当是在所有语言中都应提倡的.
>
> 最近我也在迷惑这个问题.有的C++书上说提倡使用异常,有的说坚决不用.如<Debugging Applications for Microsoft
> .NET and Microsoft Windows
>
> >作者就说坚决不要在C++中用异常.这两种观点导致了C++世界已有的代码产生了两种风格.而作为一个我这样的C++新手,似乎选哪一种方法,都郁闷.
>
> 就个人而言,我倾向使用异常,像C#一类的抛出异常,堆栈信息也可以看到.也许C++里就没有那么方便.促进C++里不赞成使用异常一方倾向.性能上我认为不足 为虑,又不是不停的触发异常.
>
> 搭车问个问题:
> 在C++编写的应用程序中,如果捕获未处理的异常?.UnhandledException 的处理可能要框架的支持.
>

> 2010/3/19 Shuo Chen <giantc...@gmail.com>

四不象

unread,
Mar 18, 2010, 10:34:28 PM3/18/10
to pon...@googlegroups.com
我一直认为C++中开启异常处理机制代价太大了,所以有人会建议不要开启C++的异常处理
C++的异常和windows的SEH是两套机制吧
windows的SEH用的是__try

Hongzhang Liu

unread,
Mar 18, 2010, 10:52:38 PM3/18/10
to pon...@googlegroups.com
捕获未处理异常可以在程序入口处catch啊,处理的话使用set_unexpected设置自己的处理函数。另外,现在的g++生成的代码对于异常不被触发时时没有任何性能损失的。

机械唯物主义 : linjunhalida

unread,
Mar 18, 2010, 10:54:02 PM3/18/10
to pon...@googlegroups.com
对了,C++异常是怎么实现的?

2010/3/19 Hongzhang Liu <hongzh...@gmail.com>

孙朝阳

unread,
Mar 18, 2010, 10:54:52 PM3/18/10
to pon...@googlegroups.com
此帖必火,留名。

抛开设计谈错误处理是不靠谱的。抛开不变式谈异常也是不靠谱的。
设计没有什么对错之分,只有是不是适合之分(牛人语录,出处请自行google)。
要说适合不适合,抛开具体环境来谈是不靠谱的。
所以,你觉得怎么做简单,好理解,就怎么做。

---------------------------------------
绝圣弃知,大盗乃止;擿玉毁珠,小盗不起;

jinhu wang

unread,
Mar 18, 2010, 11:02:00 PM3/18/10
to pon...@googlegroups.com
没错,这是典型的assert的应用场景。
说白了就是不要把问题藏到现场再去定位。

四不象

unread,
Mar 18, 2010, 11:20:22 PM3/18/10
to pon...@googlegroups.com
>现在的g++生成的代码对于异常不被触发时时没有任何性能损失的。
 
是吗,这个我倒不了解,我对C++编译器的了解还停留在VC6.0的阶段,呵呵。

jinhu wang

unread,
Mar 18, 2010, 11:17:46 PM3/18/10
to pon...@googlegroups.com
        引经据典总显得教条。
        我曾经在某公司遇到过这样的一段c程序,那段代码的诞辰大约是90年代末,作者很赶时髦,用了大量的longjmp来处理“异常”情况,当式我定位一个内存泄漏的问题,每次遇到longjmp都要在很long的一条路上反向搜索,大量的longjmp导致代码的可维护性很差。c++的异常其实就是c里的longjmp的升级版本,而longjmp是goto的升级版本。如果说goto是一个编程规范不待见的东西大家没异议吧?反推回去同样的道理。
 总的说c++的应用领域决定了他不太适合用异常。
有些语言是快餐式语言,每个错误分支不需要过度推敲,太过完美的逻辑往往会影响设计和开发速度。等你完美好了你的程序,可能你的程序生命周期已经到了。
c++很多时候是用在一些生命周期相对较长的产品上的。例如你在某家公司会发现你维护的一段代码可能是十年前某人写的。
 
To unsubscribe from this group, send email to pongba+unsubscribegooglegroups.com or reply to this email with the words "REMOVE ME" as the subject.

四不象

unread,
Mar 18, 2010, 11:27:33 PM3/18/10
to pon...@googlegroups.com
不同版本的编译器实现方式也不同吧,C++标准没有强制规定实现方式。
我只知道VC6.0的异常捕获用的是windows系统的SEH机制

XIN, Wang

unread,
Mar 19, 2010, 12:35:17 AM3/19/10
to pon...@googlegroups.com
感觉这样的说法有问题,本质上所有的条件判断(if, while, for)都是 goto 语句,程序中大量使用 setjmp / longjmp 确实会实代码难以阅读,但如果封装过后就不是这样了,用高级的抽象的封装过的工具替换底层工具本身就是很大的进步,曾经在 c 的程序里见过一种自实现的异常处理机制,基本上就是每个函数的最后一个参数为 exception_facility 其中包含了异常表(和大多数的异常实现相似),然后用 setjmp / longjmp 来跳转,当然程序员不会直接调用。

错误处理必须分成多个级别,assert,error,exception,return code

assert: 检测程序员决不应该犯的错误,如“调用此函数时Stream必须打开”

error: 极少发生的(通常是硬件)错误,遇到这种情况只能“尽人事”然后退出。

exception: 预期中可能发生的,比如访问文件没有权限,

return code: 这个就不是错误了,属于正常逻辑

上面说的是逻辑上的分类,实践中可能用一些 return code 来实现 exception.

C++ 因为没有 GC 所以使用 exception 容易造成 memory leak,Java/C# 在这方面好很多。但是对于 resource leak 这些语言都是一样的。

工具的使用还是看人,如果团队里所有人都对 C++ 的 exception 机制很熟悉(同时也对 smart ptr 等类 GC 机制很熟悉)那么使用 exception 也没什么大不了的,反之还是用最简单的机制就好。

讨论是否用 exception 要看是对个人,还是对团队。



2010/3/19 jinhu wang <wangji...@gmail.com>

四不象

unread,
Mar 19, 2010, 1:36:13 AM3/19/10
to pon...@googlegroups.com
为啥,人家微软的windows不就是这么干的嘛
--
水仙欲上鲤鱼去
一夜芙蓉红泪多

Mikster.Z

unread,
Mar 19, 2010, 2:02:57 AM3/19/10
to pon...@googlegroups.com
没记错的话人家是做电信设备的。

jinhu wang

unread,
Mar 19, 2010, 2:30:02 AM3/19/10
to pon...@googlegroups.com
首先c++抛出异常时是会调用局部变量的析构函数的,记得effective c++专门有一节比较了异常跟longjmp的区别。
根据你的说法,这么一转,也就是说你对goto的接受程度直接决定了你对异常的接受度。
没有绝对的对错,理论上能跑出预期结果的程序都是可以接受的。

jinhu wang

unread,
Mar 19, 2010, 2:31:17 AM3/19/10
to pon...@googlegroups.com
多谢记得,不过能不能把人家这个字眼换换,显得疏远了:)

Yongwei Wu

unread,
Mar 19, 2010, 6:49:01 AM3/19/10
to pon...@googlegroups.com
你没有给出例子来说明为何使用了异常是灾难。你似乎也没有仔细阅读下面的例子。

要写出代码解决我下面提到的问题,调用代码只需要这样写:

ifstream ifs(FileStreamName(filename));

如果不允许异常的话,你就不可能这样写。你得写成大概这个样子:

FileStreamName converter;
if (!converter.setFileStreamName(filename)) {
// Handles error
return false;
}
ifstream ifs(converter.getConvertedName());

这样的话,错误处理代码和主流程代码混在一起,可读性也差。

如果你不同意,请给出你认为合适的代码例子。

2010/3/19 jinhu wang <wangji...@gmail.com>:

Yongwei Wu

unread,
Mar 19, 2010, 6:49:58 AM3/19/10
to pon...@googlegroups.com
抛了异常就必须捕获,否则会core dump。没看出你说的跟我有什么矛盾。

2010/3/19 jinhu wang <wangji...@gmail.com>:

Yongwei Wu

unread,
Mar 19, 2010, 6:52:10 AM3/19/10
to pon...@googlegroups.com
事实上,我现在使用的编码规范就大量参考了Google的这一份。最大的分离点就在于我推荐使用异常。

Google有legacy code。我们没有。

2010/3/19 superzxt <supe...@gmail.com>:

> To unsubscribe from this group, send email to
> pongba+unsubscribegooglegroups.com or reply to this email with the words
> "REMOVE ME" as the subject.
>

--

Yongwei Wu

unread,
Mar 19, 2010, 7:06:40 AM3/19/10
to pon...@googlegroups.com
2010/3/19 Shuo Chen <gian...@gmail.com>:

> 问题在于,在一个多人团队里,每个人对“错误的类型”的观点很可能不一致。
>
> 如果用异常,对同一个错误,有人觉得该抛,有人觉得不该抛。
> 我的函数如果往外抛,我怎么知道接收方有没有打算接住这个异常呢?
> 如果我调用一个函数,我怎么知道它和它调用的子程序会不会抛出我没有想到的异常?(我总不能来一次广度优先遍历,把代码全查一遍吧)

会不会抛异常,会抛什么样的异常,需要文档化。Java里有编译器帮助检查,C++要靠程序员自律。

C++本来就是一个自律性要求很高的语言。

> 如果调用我的程序和我调用的程序都不是我写的,那么大家如何形成共识,不出现漏抛和漏接的情况?

如果使用错误码,都不是你写的,如何保证大家都检查错误码了?

> 如果要靠文档来约定,为什么不把文档做到代码里,用返回值(一个 enum)作为文档呢?

已经说过,是否允许函数返回对象,对程序的表达能力有极大的影响。如果你不同意,请回复我的代码例子。

> 对于一个人的项目,抛不抛都行,左右半脑直接沟通好即可。
> 对于多人项目,乱抛异常跟随处大小便没啥区别。

所以模块间的接口是项目早期就需要讨论决定的。异常定义是接口的一部分(就像错误码一样)。

Yongwei Wu

unread,
Mar 19, 2010, 7:15:22 AM3/19/10
to pon...@googlegroups.com
就你说的第一个问题,我没看到过一个C++大师说不要使用异常的:

Bjarne Stroustrup
Herb Sutter
Scott Meyers
Andrew Alexandrescu
...

搜了一下,找到了Bjarne的原话:

What good can using exceptions do for me? The basic answer is: Using
exceptions for error handling makes you code simpler, cleaner, and
less likely to miss errors. But what's wrong with "good old errno and
if-statements"? The basic answer is: Using those, your error handling
and your normal code are closely intertwined. That way, your code gets
messy and it becomes hard to ensure that you have dealt with all
errors (think "spaghetti code" or a "rat's nest of tests").

http://www2.research.att.com/~bs/bs_faq2.html#exceptions-why

2010/3/19 haozes <hao...@gmail.com>:

--

Milo Yip

unread,
Mar 19, 2010, 7:15:46 AM3/19/10
to pon...@googlegroups.com
找到一篇好文關於 exception handling 的overhead,但我未閱,可研究

Technical Report on C++ Performance
Section 5.4 Exception Handling

http://www.open-std.org/jtc1/sc22/wg21/docs/TR18015.pdf

談這個我只能從我的經驗應用上說。
在遊戲執行期通常都不會用C++ exception。
除了I/O和一些硬件相關的,遊戲不應有不能預期的錯誤。

在 2010年3月19日上午10:52,Hongzhang Liu <hongzh...@gmail.com> 寫道:
> 捕获未处理异常可以在程序入口处catch啊,处理的话使用set_unexpected设置自己的处理函数。另外,现在的g++生成的代码对于异常不被触发时时没有任何性能损失的。
>

> 2010/3/19 四不象 <tabri...@gmail.com>

--
Milo Yip

Twitter @miloyip
http://www.cnblogs.com/miloyip/
http://miloyip.seezone.net/

Yongwei Wu

unread,
Mar 19, 2010, 7:40:24 AM3/19/10
to pon...@googlegroups.com
Win32/MSVC是里面说的code approach,性能比较差。

Win64/MSVC和主流的Linux/GCC都是table approach,仅在抛出异常时有性能影响。

在性能较为关键的部分,确实应该避免使用异常。即使这种情况,对于不可恢复型的错误,如I/O、硬件、关键操作内存不足等,使用异常可以让代码更为简洁,不用处处检查返回值。

2010/3/19 Milo Yip <mil...@gmail.com>:

--

孙朝阳

unread,
Mar 19, 2010, 7:59:55 AM3/19/10
to pon...@googlegroups.com

---------------------------------------
绝圣弃知,大盗乃止;擿玉毁珠,小盗不起;


在 2010年3月19日 下午6:52,Yongwei Wu <wuyo...@gmail.com>写道:
事实上,我现在使用的编码规范就大量参考了Google的这一份。最大的分离点就在于我推荐使用异常。

Google有legacy code。我们没有。

个人认为JSF的规范比google的好:
虽然JSF中不使用异常,但是解释和原因很实际。

Shuo Chen

unread,
Mar 19, 2010, 8:22:55 AM3/19/10
to TopLanguage
哈哈,他们当然不会说,这不是自打耳光吗?

我得自己掂量掂量,Herb Hutter 比我聪明 100 倍不止。
他用的得心应手的利器,到了我手里未见得不会伤到我自己。

我承认,我只会用返回值这样的土办法,再说操作系统的 API/syscall 都不会抛异常,他们不也活下来了?

咱跟大师没法比,不敢高攀。

> > 2010/3/19 Shuo Chen <giantc...@gmail.com>

> ...
>
> 阅读更多 >>

kelviN

unread,
Mar 19, 2010, 10:41:57 AM3/19/10
to TopLanguage
操作系统的 API/syscall 都是C吧。
该用异常没什么好争论的,问题就是回到主贴内容:怎么用好它,什么时候抛什么时候接。

> ...
>
> read more >>

archer

unread,
Mar 19, 2010, 10:51:27 AM3/19/10
to pon...@googlegroups.com
异常当然好,但是C++里用代价太高。我同意楼上提到的自律性问题,我的认识是:
  • 编译器没有自动检查异常,复杂项目里,不会容易知道是否会抛出异常。
  • 你得自己管理好出错时堆上的内存,Java之类的,因为垃圾回收,简单不少。


2010/3/19 kelviN <kowo...@gmail.com>

jinhu wang

unread,
Mar 19, 2010, 11:55:57 AM3/19/10
to pon...@googlegroups.com
感觉楼主还是要技高一筹。
我接触的第一个c/c++编程规范就是告诉我不要用异常。
至于错误码,感觉约定速成的使用0标示成功,其他的觉得有必要就多定义几个。

陨落雕

unread,
Mar 19, 2010, 1:08:00 PM3/19/10
to TopLanguage
说一句,C应该也可以用setjmp, longjmp来模拟抛异常的:http://en.wikipedia.org/wiki/
Setjmp.h

> ...
>
> read more >>

kelviN

unread,
Mar 19, 2010, 1:17:16 PM3/19/10
to TopLanguage
另外组长pongba写过一篇错误处理的文章,没看过的可以先过去看看,基本和《C++CS》 错误处理与异常那部分一致。
http://blog.csdn.net/pongba/archive/2007/10/08/1815742.aspx
PS:当时看过这文一晃3年了,还是没什么长进,主要还是项目里不怎么用。。因为难用所以大家都不愿意用吧,以至于都把难用的C++当C来用。。

jinhu wang

unread,
Mar 19, 2010, 10:39:11 PM3/19/10
to pon...@googlegroups.com
难用的东西总不是什么好东西。貌似编程语言没有一个好用的

Yongwei Wu

unread,
Mar 20, 2010, 3:09:25 AM3/20/10
to pon...@googlegroups.com
活是活了,代码太难看。每做一步,都需要检查返回值。表达不精致、不紧凑。

换句话说,不使用异常,懒惰的程序员可能写出更好读的代码。

大家可以向我拍砖,但我没看到有人提出一个不用异常而又可以方便解决我原先提出的代码问题的方法:

ifstream ifs(FileStreamName(filename));

我希望大家对于“代码之美”不是光说不练吧。

吴咏炜

2010/3/19 Shuo Chen <gian...@gmail.com>:

[snipped]

jinhu wang

unread,
Mar 20, 2010, 6:17:32 AM3/20/10
to pon...@googlegroups.com
说实话是看不懂这句代码是什么意思:(
斗胆猜一下,是文件吧?
用了这么多年c++,没有用过fstream,因为感觉还是c格式的FILE好用。

jinhu wang

unread,
Mar 20, 2010, 6:30:42 AM3/20/10
to pon...@googlegroups.com
另外你强调代码之美。
我觉得每个程序员都有一套自己的审美标准,就像大家以前讨论的左右大括号的位置,成员变量的命名格式。
你以“精致、紧凑”为美。
我的代码审美观点的是:逻辑清晰,经得起测试为美。也就是质量至上吧。

所以你所谓的光说不练是让大家跟着你的节奏走,有点强迫症的感觉:)
在 2010年3月20日 下午3:09,Yongwei Wu <wuyo...@gmail.com>写道:

rockeet

unread,
Mar 20, 2010, 7:14:13 AM3/20/10
to TopLanguage
不是C FILE 好,而是 C++ iostream 太烂
C FILE 最烂之处在于 fread/fwrite

On 3月20日, 下午6时17分, jinhu wang <wangjinhu...@gmail.com> wrote:
> 说实话是看不懂这句代码是什么意思:(
> 斗胆猜一下,是文件吧?
> 用了这么多年c++,没有用过fstream,因为感觉还是c格式的FILE好用。
>

> 在 2010年3月20日 下午3:09,Yongwei Wu <wuyong...@gmail.com>写道:
>
>
>
> > 活是活了,代码太难看。每做一步,都需要检查返回值。表达不精致、不紧凑。
>
> > 换句话说,不使用异常,懒惰的程序员可能写出更好读的代码。
>
> > 大家可以向我拍砖,但我没看到有人提出一个不用异常而又可以方便解决我原先提出的代码问题的方法:
>
> > ifstream ifs(FileStreamName(filename));
>
> > 我希望大家对于“代码之美”不是光说不练吧。
>
> > 吴咏炜
>

> > 2010/3/19 Shuo Chen <giantc...@gmail.com>:

> > >> > 我用的比较多的是.NET <http://xn--css91snpdd7ge3mx3bca4032c.net/>

> > ME" as the subject.- 隐藏被引用文字 -
>
> - 显示引用的文字 -

XIN, Wang

unread,
Mar 20, 2010, 8:00:31 AM3/20/10
to pon...@googlegroups.com
能详细说一下为什 c++ iostream 不好么?以及 fread/fwrite 有什么问题?

2010/3/20 rockeet <roc...@gmail.com>

To unsubscribe from this group, send email to pongba+unsubscribegooglegroups.com or reply to this email with the words "REMOVE ME" as the subject.

archer

unread,
Mar 20, 2010, 9:00:54 AM3/20/10
to pon...@googlegroups.com
iostream还好吧,挺优雅的,不过我倒是遇到过一个bug,就是有一种特殊locale的字符,通过cout输出,会意外导致流中断。



2010/3/20 XIN, Wang <xer...@gmail.com>

Fei Yan

unread,
Mar 20, 2010, 9:09:12 AM3/20/10
to pon...@googlegroups.com
STL的用户群中,充斥了太多对IOStream的不满,很多开源的c++项目往往重写自己的FileStream

问题的关键个人揣测应该是Exceptional-Safety和File flush/sync,这两个东西的代价据说很高(有不少profiling数据表明filestream的效率比FILE低下10倍以上),重新发明的轮子大多都去掉了这个约束(或者说是优点)

TL以前有不少这方面的讨论吧,搜搜估计能找到一打。。。

我习惯用的方式是,将数据先大堆大堆的格式化到stringstream,然后一次性用ofstream写进去;
庆幸的是一直没有遇到传说中的由于iostream性能低下而造成系统瓶颈的状况

Fei Yan

unread,
Mar 20, 2010, 9:11:54 AM3/20/10
to pon...@googlegroups.com
local这玩意,如果不是默认的clocal,个人认为就不要用typedef出来的fstream了吧,毕竟它只是个模板参数的特化;
没出异常倒是幸运的了,“意外”才是正常现象

jinhu wang

unread,
Mar 20, 2010, 10:01:16 AM3/20/10
to pon...@googlegroups.com
C FILE的fread/fwrite为何不好用?


To unsubscribe from this group, send email to pongba+unsubscribegooglegroups.com or reply to this email with the words "REMOVE ME" as the subject.

Fuzhou Chen

unread,
Mar 20, 2010, 4:38:13 PM3/20/10
to pon...@googlegroups.com
我无意拍砖,但我觉得Yongwei是给了一个根本无解的问题等我们自己跳进去。C++的
标准本来就规定构造函数不能有返回值,那么所有基于返回值的策略都必然要在这里
绕路而行。这种情况下异常这种不依赖返回值而且语言内建的机制当然是最精简的办法。


至于上升到“代码之美”就有点过了。如同Jinhu所言,美的定义因人而异。就我个人而
言,简洁是美的一部分,但如果为了这个简洁而必须以一定程度上降低代码质量作代价,
那这种代码我无论如何不可能承认是美的。

我不喜欢异常的原因很大程度上就是因为抛出异常时很难找到一个效率和简洁性兼得的办
法处理动态资源释放。实际工程中我们不难知道,动态资源分配在C++中几乎没有替代者。
也许有人会争辩说我们可以用RAII,但由于赋值效率问题,RAII只能部分地代替动态资源
释放,而C++标准这么多年又没有一个大家都认可的智能指针来解决赋值问题——auto_ptr
显然不是合理的选择。


所以这样取舍下来,异常是唯一一个能够相对无痛苦地舍弃掉的功能。我承认这么做不完美,
最明显的例子就是构造函数错误相关的错误处理,但相对而言绕过这个问题也很简单,也就是
C++的fstream采用的办法:ifstream fd("XXX"); if (!fd) return ERROR;

这种做法不会给代码质量造成任何影响,最多也就是看上去难看一点。以我的标准而言,损失
几乎可以忽略不计。

2010/3/20 Yongwei Wu <wuyo...@gmail.com>:

> To unsubscribe from this group, send email to pongba+unsubscribegooglegroups.com or reply to this email with the words "REMOVE ME" as the subject.
>

--
《采莲》·江南

为卿采莲兮涉水,为卿夺旗兮长战。为卿遥望兮辞宫阙,为卿白发兮缓缓歌。

另抄自蒜头的评论:http://www.douban.com/review/1573456/

  且祭一束紫琳秋,为一段落花流水的传说
  且饮一杯青花酒,为一场几多擦肩的错过
  且焚一卷旖旎念,为一腔抛付虚无的惜怜
  且歌一曲罢箜篌,为一刻良辰春宵的寂寞

Yongwei Wu

unread,
Mar 20, 2010, 9:23:46 PM3/20/10
to pon...@googlegroups.com
罗嗦肯定不美,这个大家应该没有疑问。剩下的问题是,在性能、复杂度、可理解性之间取一个权衡。

主要分歧点在于,我完全不认为这种简洁降低了代码质量或可读性。如果你说性能,我还勉强可以接受(至少在Win32/MSVC的情况下是事实)。我觉得异常的主要问题是需要换思维的方式。确实不容易,但已经有不少书讨论如何写异常安全的代码,和常见的惯用法(auto_ptr/shared_ptr,swap成员,等等)。这在Sutter和Meyers的书里都有讨论。新的标准里的shared_ptr和移动语义也让返回对象变得更为方便。如果你非要返回错误码的话,我会觉得这是对语言功能的浪费。:-)

吴咏炜

On Sunday, March 21, 2010, Fuzhou Chen <cppo...@gmail.com> wrote:
> 我无意拍砖,但我觉得Yongwei是给了一个根本无解的问题等我们自己跳进去。C++的
> 标准本来就规定构造函数不能有返回值,那么所有基于返回值的策略都必然要在这里
> 绕路而行。这种情况下异常这种不依赖返回值而且语言内建的机制当然是最精简的办法。
>
>
> 至于上升到"代码之美"就有点过了。如同Jinhu所言,美的定义因人而异。就我个人而
> 言,简洁是美的一部分,但如果为了这个简洁而必须以一定程度上降低代码质量作代价,
> 那这种代码我无论如何不可能承认是美的。
>
> 我不喜欢异常的原因很大程度上就是因为抛出异常时很难找到一个效率和简洁性兼得的办
> 法处理动态资源释放。实际工程中我们不难知道,动态资源分配在C++中几乎没有替代者。
> 也许有人会争辩说我们可以用RAII,但由于赋值效率问题,RAII只能部分地代替动态资源

> 释放,而C++标准这么多年又没有一个大家都认可的智能指针来解决赋值问题----auto_ptr

>>>> > 我用的比较多的是.NET <http://xn--css91snpdd7ge3mx3bca4032c.NET>之类的,或者比较新一点的语言都有介绍如何正确使用异常.到底何时抛异常,也是没有那么绝对的问题.可以参照那些.

jinhu wang

unread,
Mar 20, 2010, 10:09:17 PM3/20/10
to pon...@googlegroups.com
罗嗦和严谨不冲突,因为简洁而丢掉严谨是不对的。所以你的罗嗦不美并不总成立
在 10-3-21,Yongwei Wu<wuyo...@gmail.com> 写道:

archer

unread,
Mar 21, 2010, 2:31:55 AM3/21/10
to pon...@googlegroups.com
我觉得还要加上可扩展性的平衡, 没有显示的编译器检查,很容易漏掉异常处理. 代码简单的时候,还可以手工去检查是否需要处理异常.复杂的情况下,很难. 另外就是专家建议的很多东西, 在实际代码中不是完美实施的.

2010/3/21 Yongwei Wu <wuyo...@gmail.com>



--
Best Regards,

Archer

Ming Zhe Huang

Fuzhou Chen

unread,
Mar 21, 2010, 3:20:07 AM3/21/10
to pon...@googlegroups.com
你说的分歧点并不成立。我的意见是:

a) 我从没有说过简洁降低代码质量。但有一点我想大家能够同意:就是在
存在异常的情况下,动态资源的管理比不允许使用异常时要困难。换句
话说,对同一个程序员而言,他/她出错的可能性要大一些。问题无法要求
组员完全理解异常——我怎么去验证呢?但是相对而言,要求不使用异常
怎容易操作许多。

b) 我绝对坚持一点:异常的所谓“简洁”实际上降低了代码的可读性。因为异
常使得代码的执行逻辑和阅读时的印象不一致。尤其是C++没有像Java那
样要求当被调函数存在异常列表时强制要求主调函数进行异常检查,所以
在读代码时很难知道那些地方可能导致程序退出。如果是自己写的代码还好
些,因为相对熟悉;但对那些日后需要来维护你的代码的程序员来说,他们
的阅读负担要大很多。

至于性能,就我所知G++的异常处理代码在good path上没有额外开销,所以
这个应该可以放心。CL的情况不太清楚,希望有高人指导一下。

至于Scott Mayers或者Hurb Sutter的书,我也读过一些,他们的技巧都没有
错,但我的看法是如果一个语言特性在默认的使用场景中就需要小心地规避其他
的语言特性——不妨对比Java、Python和C#——那么这个语言特性的合理性就
是值得商榷的。我承认这是一种浪费,但如果这种浪费使得我能更方便地写出没
有bug的代码,那就让它浪费好了。

最后重复一次:我个人认为异常和C++风格的手工动态资源分配是冲突的,两者
都保留确实可行,但这对程序员要求太高;只要放弃一样,则程序员就能轻松很多。
而手工动态分配资源又是不能放弃的,所以对我而言放弃异常就是唯一的选择。当
然,不同的开发组观点不同,我并不是试图说服你改变看法。

2010/3/20 Yongwei Wu <wuyo...@gmail.com>:

Yongwei Wu

unread,
Mar 21, 2010, 7:32:20 AM3/21/10
to pon...@googlegroups.com
罗嗦和严谨不冲突,但和美冲突。

另外,一般研究表明,代码的错误数和行数成正比,而不是和完成的功能量成正比。也就是说,简洁的代码的错误更少。

我也从来没有丢掉严谨过。相反,我说过异常是接口的一部分,需要尽早定义。

>>>>>> > <http://xn--css91snpdd7ge3mx3bca4032c.NET>之类的,或者比较新一点的语言都有介绍如何正确使用异常.到底何时抛异常,也是没有那么绝对的问题.可以参照那些.

Yongwei Wu

unread,
Mar 21, 2010, 7:35:11 AM3/21/10
to pon...@googlegroups.com
同意异常比较难用,且经常被误解。我觉得可以通过学习和培训解决。

On Sunday, March 21, 2010, Fuzhou Chen <cppo...@gmail.com> wrote:
> 你说的分歧点并不成立。我的意见是:
>
> a) 我从没有说过简洁降低代码质量。但有一点我想大家能够同意:就是在
> 存在异常的情况下,动态资源的管理比不允许使用异常时要困难。换句
> 话说,对同一个程序员而言,他/她出错的可能性要大一些。问题无法要求

> 组员完全理解异常----我怎么去验证呢?但是相对而言,要求不使用异常


> 怎容易操作许多。
>
> b) 我绝对坚持一点:异常的所谓"简洁"实际上降低了代码的可读性。因为异
> 常使得代码的执行逻辑和阅读时的印象不一致。尤其是C++没有像Java那
> 样要求当被调函数存在异常列表时强制要求主调函数进行异常检查,所以
> 在读代码时很难知道那些地方可能导致程序退出。如果是自己写的代码还好
> 些,因为相对熟悉;但对那些日后需要来维护你的代码的程序员来说,他们
> 的阅读负担要大很多。
>
> 至于性能,就我所知G++的异常处理代码在good path上没有额外开销,所以
> 这个应该可以放心。CL的情况不太清楚,希望有高人指导一下。
>
> 至于Scott Mayers或者Hurb Sutter的书,我也读过一些,他们的技巧都没有
> 错,但我的看法是如果一个语言特性在默认的使用场景中就需要小心地规避其他

> 的语言特性----不妨对比Java、Python和C#----那么这个语言特性的合理性就

>>>>>> > 我用的比较多的是.NET <http://xn--css91snpdd7ge3mx3bca4032c.NET> <http://xn--css91snpdd <http://xn--css91snpdd7ge3mx3bca4032c.NET>

XIN, Wang

unread,
Mar 21, 2010, 7:57:03 AM3/21/10
to pon...@googlegroups.com
关于 “一般研究表明,代码的错误数和行数成正比” 我总是持怀疑态度,不是怀疑这个事实,而是怀疑由此得出的结论。这里面有两个问题,1,代码行数少并不意味着代码简洁。2,错误的数量不代表 Fix 错误的成本。

2010/3/21 Yongwei Wu <wuyo...@gmail.com>

jinhu wang

unread,
Mar 21, 2010, 9:47:42 AM3/21/10
to pon...@googlegroups.com
有点口水战的感觉,哈哈。
越来越觉得这个辩论没有意义:)

孙朝阳

unread,
Mar 21, 2010, 9:58:10 AM3/21/10
to pon...@googlegroups.com
关键是使用异常需要一整套的技能,任何一个主要部分的缺失都会导致严重的问题。而能够把这几个部分都实践好的,少之又少。这就是为什么很多人说异常不好,而大牛却无人否定异常的原因。

在C++中,1.写异常安全的代码 2.契约式程序设计3.严守异常安全的三个等级。缺了任何一个,都是噩梦。


---------------------------------------
绝圣弃知,大盗乃止;擿玉毁珠,小盗不起;


Fuzhou Chen

unread,
Mar 21, 2010, 3:01:49 PM3/21/10
to pon...@googlegroups.com
对,这也就是我想说的。

我不是说异常不该用。正如我在之前的一些帖子里说的一样,一个
工程师面前没有该不该用的问题,只有值不值得用的问题。大家都
能看到我的回复里一直在不停地对错误值和异常做比较,最后的结
论是用异常的代价比较大,所以它不值得用。顺便说一句:我的结
论仅仅针对C++,而Java/C#/Python不在此列。

这里的代价不是代码效率——我敢说大部分如今关于效率的讨论都
是借口——真正的开销出在Yongwei说的培训上。你觉得谁来做这
个培训?培训多久?内容多少?还有最重要的:你怎么验证你的组
员已经合格了?我相信最精确的验证是写好了程序让它在客户那里
转一年不出错,可哪个公司愿意冒这个风险?


至于代码行数和错误数的关系——

a) 其实我也一样怀疑行数和错误数成正比的结论。
b) 就C/C++而言,我们可以用宏或内联函数来简化错误处理代码。
c) 如果对比异常和错误返回,你也许会发现异常根本没有减少代码
行数,因为你必须在别的某处写代码处理这些错误。而且由于这
个处理错误的地方和出错点不一样,你可能还需要更多的代码让
你的异常类变得更复杂,以尽量多地保留现场信息以备调试,结
果是让程序员的负担进一步加重。

所以即使按照你的说法,我还是可以得出异常不值得使用的结论。

2010/3/21 孙朝阳 <wing...@gmail.com>:

--

Fuzhou Chen

unread,
Mar 21, 2010, 3:47:01 PM3/21/10
to pon...@googlegroups.com
不好意思,没有看到Yongwei的原始回复。只好再回复一次。

我同意这一点:如果你已经决定在代码里使用异常,那么你说的是对的。
异常规范需要尽早定义,而且人员也需要培训,否则迟早会出问题。

第二,Yongwei你之前说过你的项目没有历史负担。OK,那么我相信你
使用异常会比我的场景安全很多。比如我说的RAII的问题,如果你的项目
是新的,那么你就会有可能引入shared_ptr之类的机制来避免手工资源
管理,从而弥补C++异常的一个主要问题。我们的的区别在于我做过的所
有项目都有五到十年的历史负担,所以我每加一行代码都要考虑旧代码会
不会受影响,而且我也没有随便引入新的函数库的自由。

第三,我一直没有提到的一点是我个人不太喜欢异常这种机制:我认为异
常打乱了原本的代码层次关系,让上层的主调函数可能必须处理数层以下
的被调函数的错误。这使得原本我们定义子函数的目的之一,即抽象操作,
被破坏了,因为就我而言,我很难在不清楚上下文的情况下合理地处理错
误。从这个意义上说我认为它与随便使用goto有相似的问题。

——当然,第三点纯属个人看法,不代表大众观点。请各位拍砖的同仁不
要把俺和Hurb Sutter那些大牛的言论对比,不然那些大牛太吃亏了。

最后一点:我不同意把“美”这个没有明确定义过的含糊词汇引入计算机
这个领域。我们写程序是为了完成一个功能并尽量希望做到没有bug
(当然,这个很难),而不是为了“美”。站在我这种编码工匠的立场,
与其引入一个“美”的功能并带来额外的培训开销和出错风险,还不如用
一些“丑一点”但可靠的东西。

2010/3/21 Yongwei Wu <wuyo...@gmail.com>:

jinhu wang

unread,
Mar 21, 2010, 10:12:59 PM3/21/10
to pon...@googlegroups.com
如果是没有历史负担的项目,那就尽情施展尽量多的技术和技巧吧:)
对个人来说总是没有太大坏处的。
 
“每个人都会经过这个阶段,见到一座山,就想知道山后面是什么...”

在 2010年3月21日 下午7:35,Yongwei Wu <wuyo...@gmail.com>写道:

机械唯物主义 : linjunhalida

unread,
Mar 21, 2010, 10:59:18 PM3/21/10
to pon...@googlegroups.com
然后这个项目就产生了很多历史负担。。

2010/3/22 jinhu wang <wangji...@gmail.com>

jinhu wang

unread,
Mar 21, 2010, 11:01:46 PM3/21/10
to pon...@googlegroups.com
那是翻过山以后的事,哈哈

Eric.Wang

unread,
Mar 22, 2010, 1:41:22 AM3/22/10
to TopLanguage
这就是我认为的不负责任的态度。
把公司的项目当成你的试验田了?项目失败了怎么办?项目难以维护怎么办?你都不考虑的?

要选对公司有益的技术,而不是选对你个人有益的技术。这才是负责任的做法。


On Mar 22, 10:12 am, jinhu wang <wangjinhu...@gmail.com> wrote:
> 如果是没有历史负担的项目,那就尽情施展尽量多的技术和技巧吧:)
> 对个人来说总是没有太大坏处的。
>
> "每个人都会经过这个阶段,见到一座山,就想知道山后面是什么..."
>

> 在 2010年3月21日 下午7:35,Yongwei Wu <wuyong...@gmail.com>写道:
>
> > 同意异常比较难用,且经常被误解。我觉得可以通过学习和培训解决。
>

> > On Sunday, March 21, 2010, Fuzhou Chen <cppof...@gmail.com> wrote:
> > > 你说的分歧点并不成立。我的意见是:
>
> > > a) 我从没有说过简洁降低代码质量。但有一点我想大家能够同意:就是在
> > > 存在异常的情况下,动态资源的管理比不允许使用异常时要困难。换句
> > > 话说,对同一个程序员而言,他/她出错的可能性要大一些。问题无法要求
> > > 组员完全理解异常----我怎么去验证呢?但是相对而言,要求不使用异常
> > > 怎容易操作许多。
>
> > > b) 我绝对坚持一点:异常的所谓"简洁"实际上降低了代码的可读性。因为异
> > > 常使得代码的执行逻辑和阅读时的印象不一致。尤其是C++没有像Java那
> > > 样要求当被调函数存在异常列表时强制要求主调函数进行异常检查,所以
> > > 在读代码时很难知道那些地方可能导致程序退出。如果是自己写的代码还好
> > > 些,因为相对熟悉;但对那些日后需要来维护你的代码的程序员来说,他们
> > > 的阅读负担要大很多。
>
> > > 至于性能,就我所知G++的异常处理代码在good path上没有额外开销,所以
> > > 这个应该可以放心。CL的情况不太清楚,希望有高人指导一下。
>
> > > 至于Scott Mayers或者Hurb Sutter的书,我也读过一些,他们的技巧都没有
> > > 错,但我的看法是如果一个语言特性在默认的使用场景中就需要小心地规避其他
> > > 的语言特性----不妨对比Java、Python和C#----那么这个语言特性的合理性就
> > > 是值得商榷的。我承认这是一种浪费,但如果这种浪费使得我能更方便地写出没
> > > 有bug的代码,那就让它浪费好了。
>
> > > 最后重复一次:我个人认为异常和C++风格的手工动态资源分配是冲突的,两者
> > > 都保留确实可行,但这对程序员要求太高;只要放弃一样,则程序员就能轻松很多。
> > > 而手工动态分配资源又是不能放弃的,所以对我而言放弃异常就是唯一的选择。当
> > > 然,不同的开发组观点不同,我并不是试图说服你改变看法。
>

> > > 2010/3/20 Yongwei Wu <wuyong...@gmail.com>:


> > >> 罗嗦肯定不美,这个大家应该没有疑问。剩下的问题是,在性能、复杂度、可理解性之间取一个权衡。
>
> > 主要分歧点在于,我完全不认为这种简洁降低了代码质量或可读性。如果你说性能,我还勉强可以接受(至少在Win32/MSVC的情况下是事实)。我觉得异常的主要问题是需要换思维的方式。确实不容易,但已经有不少书讨论如何写异常安全的代码,和常见的惯用法(auto_ptr/shared_ptr,swap成员,等等)。这在Sutter和Meyers的书里都有讨论。新的标准里的shared_ptr和移动语义也让返回对象变得更为方便。如果你非要返回错误码的话,我会觉得这是对语言功能的浪费。:-)
>
> > >> 吴咏炜
>

> > >> On Sunday, March 21, 2010, Fuzhou Chen <cppof...@gmail.com> wrote:
> > >>> 我无意拍砖,但我觉得Yongwei是给了一个根本无解的问题等我们自己跳进去。C++的
> > >>> 标准本来就规定构造函数不能有返回值,那么所有基于返回值的策略都必然要在这里
> > >>> 绕路而行。这种情况下异常这种不依赖返回值而且语言内建的机制当然是最精简的办法。
>
> > >>> 至于上升到"代码之美"就有点过了。如同Jinhu所言,美的定义因人而异。就我个人而
> > >>> 言,简洁是美的一部分,但如果为了这个简洁而必须以一定程度上降低代码质量作代价,
> > >>> 那这种代码我无论如何不可能承认是美的。
>
> > >>> 我不喜欢异常的原因很大程度上就是因为抛出异常时很难找到一个效率和简洁性兼得的办
> > >>> 法处理动态资源释放。实际工程中我们不难知道,动态资源分配在C++中几乎没有替代者。
> > >>> 也许有人会争辩说我们可以用RAII,但由于赋值效率问题,RAII只能部分地代替动态资源
> > >>> 释放,而C++标准这么多年又没有一个大家都认可的智能指针来解决赋值问题----auto_ptr
> > >>> 显然不是合理的选择。
>
> > >>> 所以这样取舍下来,异常是唯一一个能够相对无痛苦地舍弃掉的功能。我承认这么做不完美,
> > >>> 最明显的例子就是构造函数错误相关的错误处理,但相对而言绕过这个问题也很简单,也就是
> > >>> C++的fstream采用的办法:ifstream fd("XXX"); if (!fd) return ERROR;
>
> > >>> 这种做法不会给代码质量造成任何影响,最多也就是看上去难看一点。以我的标准而言,损失
> > >>> 几乎可以忽略不计。
>

> > >>> 2010/3/20 Yongwei Wu <wuyong...@gmail.com>:


> > >>>> 活是活了,代码太难看。每做一步,都需要检查返回值。表达不精致、不紧凑。
>
> > >>>> 换句话说,不使用异常,懒惰的程序员可能写出更好读的代码。
>
> > >>>> 大家可以向我拍砖,但我没看到有人提出一个不用异常而又可以方便解决我原先提出的代码问题的方法:
>
> > >>>> ifstream ifs(FileStreamName(filename));
>
> > >>>> 我希望大家对于"代码之美"不是光说不练吧。
>
> > >>>> 吴咏炜
>

> > >>>> 2010/3/19 Shuo Chen <giantc...@gmail.com>:

> > >>>>>> > 我用的比较多的是.NET <http://xn--css91snpdd7ge3mx3bca4032c.net/> <
> >http://xn--css91snpdd7ge3mx3bca4032c.NET<http://xn--css91snpdd7ge3mx3bca4032c.net/>>
> > <http://xn--css91snpdd<http://xn--css91snpdd7ge3mx3bca4032c.NET<http://xn--css91snpdd7ge3mx3bca4032c.net/>

jinhu wang

unread,
Mar 22, 2010, 1:45:32 AM3/22/10
to pon...@googlegroups.com
如果Yongwei 同学是你的头,你怎么说服他?哈哈

To unsubscribe from this group, send email to pongba+unsubscribegooglegroups.com or reply to this email with the words "REMOVE ME" as the subject.

Fuzhou Chen

unread,
Mar 22, 2010, 5:20:53 AM3/22/10
to pon...@googlegroups.com
我想我可能无法说服他。按照我的习惯,我倾向于全组投票表决。

从我个人的经验来说,有时候我发现我很难向开发人员解释漂亮的
技巧对维护人员带来的负担。有时候我和公司产品组的同事辩论失
败之后甚至不无恶意地想:要是让你们也到我们这里干两年,自己
看看那些几乎无从下手的bug,也许你们就老实了,嘿嘿。


其实C++的异常相对而言还算好的。我见过更糟糕的,比如滥用
全局钩子,比如滥用多线程,比如滥用WaitForMultipleObjects()。
这些问题的一个共有特性是这些代码的执行状况与代码阅读时的印
象不统一,代码复杂时的执行路径近乎于一团乱麻。但偏偏这些代
码还真能引经据典地按Design Pattern写得一板一眼,结果是我们
维护人员的抱怨都不会有人赞同,非常无奈。

2010/3/21 jinhu wang <wangji...@gmail.com>:

--

孙朝阳

unread,
Mar 22, 2010, 5:39:49 AM3/22/10
to pon...@googlegroups.com
首先摆明一下我的立场。我是支持使用异常的,我对异常的任何批评不影响我的立场。

先说培训。我不知道怎么培训一大群人,也不知道效果如何。但是,对一个小的C++项目team(10规模以下),如果我是负责人,队友的C++技能也过得去的话, 那么,培训的成本可以说非常之低。正规的课程最多两天,再利用code review来练习一段时间即可。当然,这也有个条件,新项目或者小项目可行。大的遗留项目咱没有经验。

再说概念问题。我认为有必要澄清几个概念。一个是错误。返回错误码,叫错误;程序逻辑错,也叫错误;有些人把异常也叫错误。这几个东西很显然是不同的东西。我习惯于把程序逻辑错误叫做错误。换句话说,错误就是程序员脑子进水而导致的逻辑矛盾。当然,作为一个合格的非食草偶蹄类程序员,我们应该勇于承认自己天天脑子进水。

对应到现实,C/C++中的断言,Java的不可捕获异常,就是处理我所谓错误的机制。说到异常,我认为有必要区分异常的概念和实现,后面再做比较。对于错误,我认为终止程序是唯一理性的选择----体面点说,叫做优雅地退出(在我看来,许多场合abort足够优雅)。不那么可靠的办法是,禁止所有和错误点关联的功能。这个有点难,但是不那么刺耳。只是我想说的是,你真有代码能预防脑子抽筋儿干蠢事吗?
工程上,对于错误,我们能做的,一是尽可能多,尽可能早地发现,二是一旦发现,就是尽量保留现场做尸检。

什么是异常?我当初是看到这样的定义,才真正理解异常的:
当前条件满足,后条件无法达成的时候,抛出异常。
我们混关系,异常混条件,天经地义。还好,我在看到这句话的时候,已经读过了《Design by Contract》。很多人把DBC的的职责划分当作实现手段,在我看来,简直是买椟还珠。书的名字写着大大的Design嘛!按照我的理解,DBC是受卢梭的《社会契约论》启发地的一种程序设计哲学。DBC把这个几个条件讲的还是很清楚的。按照DBC的意思,前、后条件是接口的一部分。契约甚至是最重要的可形式化部分----功能不能形式化,函数签名只是表达。

既然契约是一种设计,而契约的支配性决定了异常如何产生,所以我要说,脱离了接口设计谈怎么处理异常毫无意义(好吧,我承认还是有一点意义的,就是大家都认为异常不能用)。请注意,我说的异常概念和语言定义的异常是不一致的。按照我的说法,Java的不可捕获异常不是异常(好绕),而是错误。更好的说法是,出现错误时,才使用不可捕捉异常。无论异常在不同的语言如何实现,使用原则是一样的。

现在可以解释一下,为什么遗留项目用不了异常安全?因为你改变不了接口,因为政治的,经济的,工程的等等原因。甚至,仅仅是因为缺乏勇气----在我看来这是第二障碍。第一障碍是自己或者队友的无知,不怕神一样的对手,就怕猪一样的队友嘛。

那些负责新项目的程序员,如果你没有设计接口的权力,而给你的那些接口又限制很大的话,同样痛苦。所以,就算你拿到一个新项目,也别得意,以为就一定可以使用异常了。

要使用异常,有了我说的这些概念是不够的。在C++中,必须要写异常安全的代码才行。我是不知道怎么样才能让异常安和非异常安全的代码一起工作。我虽然看过一些(好像是Herb,不确定)关于怎么从不是异常安全的代码向异常安全的改进的文章。鉴于我对设计--异常一体的看法,我不认为这种改进能取得根本性的代码质量提升。所以,我从没深入尝试过。

我认为优美的代码和优美的论文一样,所说的就是所要表达的。Yongwei的代码简炼而直白,所以就是优美的。一段代码一定有个主线逻辑,那种学生式的直白的代码,在我看来是最好的风格,删繁就简三秋树嘛。程序员的和学生代码的差别在于,学生的代码往往是错的,而程序员的则是对的。换句话说,程序员的代码应该是深思熟虑的直白。

返回码是肮脏,混乱,偷渡专家。99%的异常安全代码不需要try...catch.而100%的返回码,都应该被检查. 检查返回码导致大量的非主线逻辑的代码,我在多个项目的代码中,看到过大量超过60%(保守)的非主线逻辑的代码。这样的代码,没有大勇气,大无畏精神是读不下去的。返回值忘记检查也不鲜见,问题也是多多。那种强制检查返回值的程序库在C++都出现了(请搜索DDJ),问题严重性可想而知。如果涉及到资源分配,还要写一坨一坨臭不可闻的资源释放的代码块,我都快吐了。这还不够,还要把返回码像接力一样,一级一级往上传。就算最土的try...catch,也不需要叠罗汉样的接力吧?

当然,返回码方式也可以用RAII来释放资源。可是,如果你连RAII都用了,使用异常的技术性障碍就不多了。不过就是写异常安全代码而已,95%的时间里,这个很容易。困难的,只是那5%。至于说异常也要检查什么的,我都懒得说了,不要纸上谈兵。找一个正确使用了异常的项目,grep一下catch有多少行,throw又有多少行就知道了。返回码的if检查高出一个数量级都不止。把一个500行的基于返回码的函数,用异常后降低到200行所带来的愉悦,那是可以比拟ML的。不只是愉悦代码行的减少,清晰而突出的主线逻辑拯救了大量脑细胞。

说异常不能减少代码行数的是没有根据的,事实和理论都不支持这个说法。

既然我站在使用异常这一边,那就要为异常鼓吹一下。
那些热爱FP的信徒们,你们有福了,异常安全解放了返回值
再次强调,异常是设计决定的。


---------------------------------------
绝圣弃知,大盗乃止;擿玉毁珠,小盗不起;


在 2010年3月22日 上午3:01,Fuzhou Chen <cppo...@gmail.com>写道:
对,这也就是我想说的。

我不是说异常不该用。正如我在之前的一些帖子里说的一样,一个
工程师面前没有该不该用的问题,只有值不值得用的问题。大家都
能看到我的回复里一直在不停地对错误值和异常做比较,最后的结
论是用异常的代价比较大,所以它不值得用。顺便说一句:我的结
论仅仅针对C++,而Java/C#/Python不在此列。

这里的代价不是代码效率----我敢说大部分如今关于效率的讨论都
是借口----真正的开销出在Yongwei说的培训上。你觉得谁来做这

个培训?培训多久?内容多少?还有最重要的:你怎么验证你的组
员已经合格了?我相信最精确的验证是写好了程序让它在客户那里
转一年不出错,可哪个公司愿意冒这个风险?


至于代码行数和错误数的关系----

roc lee

unread,
Mar 22, 2010, 6:44:47 AM3/22/10
to pon...@googlegroups.com
在 2010年3月22日 下午5:39,孙朝阳 <wing...@gmail.com>写道:
首先摆明一下我的立场。我是支持使用异常的,我对异常的任何批评不影响我的立场。

先说培训。我不知道怎么培训一大群人,也不知道效果如何。但是,对一个小的C++项目team(10规模以下),如果我是负责人,队友的C++技能也过得去的话, 那么,培训的成本可以说非常之低。正规的课程最多两天,再利用code review来练习一段时间即可。当然,这也有个条件,新项目或者小项目可行。大的遗留项目咱没有经验。

再说概念问题。我认为有必要澄清几个概念。一个是错误。返回错误码,叫错误;程序逻辑错,也叫错误;有些人把异常也叫错误。这几个东西很显然是不同的东西。我习惯于把程序逻辑错误叫做错误。换句话说,错误就是程序员脑子进水而导致的逻辑矛盾。当然,作为一个合格的非食草偶蹄类程序员,我们应该勇于承认自己天天脑子进水。

对应到现实,C/C++中的断言,Java的不可捕获异常,就是处理我所谓错误的机制。说到异常,我认为有必要区分异常的概念和实现,后面再做比较。对于错误,我认为终止程序是唯一理性的选择----体面点说,叫做优雅地退出(在我看来,许多场合abort足够优雅)。不那么可靠的办法是,禁止所有和错误点关联的功能。这个有点难,但是不那么刺耳。只是我想说的是,你真有代码能预防脑子抽筋儿干蠢事吗?
工程上,对于错误,我们能做的,一是尽可能多,尽可能早地发现,二是一旦发现,就是尽量保留现场做尸检。

什么是异常?我当初是看到这样的定义,才真正理解异常的:
当前条件满足,后条件无法达成的时候,抛出异常。

什么情况下前条件满足,后条件无法达到?这同样是设计时的取舍。比如读取文件,如果前条件是“文件名合法”,后条件是“读出整个文件内容并返回“,那么在读取内容过程中如果失败(假设有这样几种失败可能:打开文件失败,系统 API 临时性错误,内存不足等),都可以作为异常抛出。但是如果后条件是:1)打开文件失败则返回错误;2)系统 API 临时性错误则返回错误;3) 内存不足则返回错误;4) 成功读取返回文件内容。后条件还会不满足么?
 

Fuzhou Chen

unread,
Mar 22, 2010, 7:01:01 AM3/22/10
to pon...@googlegroups.com
>
> 说异常不能减少代码行数的是没有根据的,事实和理论都不支持这个说法。
>

我反复读了两遍,看到最后才发现,原来这么长的一段话是在反对我的说法啊
我差点就不想回复了,呵呵。

====

老实说我不知道朝阳所说的“事实”和“理论”说的是什么。因为你实际上
没有写出事实,比如下面这个例子:

>
> 如果涉及到资源分配,还要写一坨一坨臭不可闻的资源释放的代码块,
> 我都快吐了。这还不够,还要把返回码像接力一样,一级一级往上传。
> 就算最土的try...catch,也不需要叠罗汉样的接力吧?
>

我对你的论断中有一点觉得很奇怪:你似乎是在暗示我们,如果你的代码
中在处理错误时需要写释放资源的代码块,则使用返回值检查的时候必须
把这些代码写上,而用异常就可以省掉这个步骤。可是这个结论你其实没
有论证。我的看法是,如果你真的需要把异常向上抛出,那么你也一样需
要把当前已经获取的资源释放掉,对么?而且你跨越多少级调用,就得负
责确保多少层的资源释放的正确性。这也是为什么我说异常无法减少代码
的原因。也许你的意思是说我们配合RAII来解决这个问题,但基于返回值
的错误处理一样可以利用RAII。


顺便说一句:你上面这段话实在找不到正常辩论中应有的礼貌。无论
我对对方的观点多么不同意,我也不会用“臭不可闻”或者“我都快吐了”
这样的说法。

另外一点是你的理论给了不少,社会契约论都引用到了,却没有
支撑你的结论。比如:


>
> 许多场合abort足够优雅。
>

不好意思,可我想你也许曲解了这句话。就我所知,所谓优雅的退出是
Erlang那边的提法,但我认为它的含义是不要试图掩盖错误或在确实
无法恢复时进行徒劳的努力。但我相信任何一个程序退出之前都得经
过一些必须的步骤,比如提示错误出错、比如尽可能地保存用户的数据。
我相信在任何一个严肃的开发项目中,出错时直接调用abort()退出程序
只会给用户带来不快甚至恐慌——这个你同意么?

也恰恰是因为上面这个观点,所以我很不理解你的回复中所谓的“不可捕
获异常”究竟是什么。在我看来如果一个程序的设计者在程序中使用了异常
却没有捕捉,那么这个行为应该叫做bug,和程序员忘了检查返回值导致
后续调用出错是一个性质。


>
> 什么是异常?我当初是看到这样的定义,才真正理解异常的:
> 当前条件满足,后条件无法达成的时候,抛出异常。
>

我想我看的书不多,不知道这句话是谁说的(有点像Bertrand Mayer
的口吻)。但很遗憾这话在我看来其实没有回答任何问题。我看不出
“当前条件满足,后条件无法达成”和返回一个错误码究竟有多少差别。
在我看来,说到底这两个问题都是表示当前逻辑无法继续,应当终止
并向上级报告。既然两者的结果是一样的,那么两者就必须处理一样
的事情,就是把当前上下文中已经占用的资源释放掉,如果必要的话,
给出出错的log。

如果做的事情都一样,那么我的确看不出异常能够节省大量的代码,除
了一种情况,那就是如果直接上级不关心这个错误而希望把它交给上上
级处理,那么没错,我承认这一点上异常的方案确实能省掉一些if (xxx)
{ ERROR } 的代码,但我们仍然要保证直接上级代码甚至是上上级的
在异常存在情况下的资源释放,换言之,不能节省程序员的精力。另外,
我的看法是距离中断地点越远,获得上下文的难度就越大,相对合理地
处理错误难度就越高。这个我之前也提到过了。解决这个问题的一个方案
就是扩充异常对象,让它容纳更丰富的信息——但这就意味着我得在异常
类的设计中写更多的代码。


P.S.:就我个人而言,我其实也不是很喜欢Bertrand Mayer的书,我
的印象是他有时候倾向于过度发明名词,比如区别错误和异常。而这些
名词并没有澄清问题,反而使他的书有时候很难理解。


>
>(好吧,我承认还是有一点意义的,就是大家都认为异常不能用)
>

这话说得无厘头,我自问我没说过异常不能用。我只是说具体到C++,
我的经验是异常对程序员的要求比较高,不值得大量引用。而且我也说
了我有经验的恰恰是遗产代码,而Yongwei所说的新代码中使用异常,我
也是肯定了使用异常的风险会小于遗产代码。——莫非我理解错误?


最后一点,只是我个人的经历:我确实没有经历过10人以下的项目开发。我
现在工作的开发组超过三百人,所以我不知道一个针对10个人的培训能不能等
量放大到三百人还能保持一样的效率。

2010/3/22 孙朝阳 <wing...@gmail.com>:

孙朝阳

unread,
Mar 22, 2010, 7:28:29 AM3/22/10
to pon...@googlegroups.com

---------------------------------------
绝圣弃知,大盗乃止;擿玉毁珠,小盗不起;


在 2010年3月22日 下午6:44,roc lee <roc.l...@gmail.com>写道:


在 2010年3月22日 下午5:39,孙朝阳 <wing...@gmail.com>写道:

首先摆明一下我的立场。我是支持使用异常的,我对异常的任何批评不影响我的立场。

先说培训。我不知道怎么培训一大群人,也不知道效果如何。但是,对一个小的C++项目team(10规模以下),如果我是负责人,队友的C++技能也过得去的话, 那么,培训的成本可以说非常之低。正规的课程最多两天,再利用code review来练习一段时间即可。当然,这也有个条件,新项目或者小项目可行。大的遗留项目咱没有经验。

再说概念问题。我认为有必要澄清几个概念。一个是错误。返回错误码,叫错误;程序逻辑错,也叫错误;有些人把异常也叫错误。这几个东西很显然是不同的东西。我习惯于把程序逻辑错误叫做错误。换句话说,错误就是程序员脑子进水而导致的逻辑矛盾。当然,作为一个合格的非食草偶蹄类程序员,我们应该勇于承认自己天天脑子进水。

对应到现实,C/C++中的断言,Java的不可捕获异常,就是处理我所谓错误的机制。说到异常,我认为有必要区分异常的概念和实现,后面再做比较。对于错误,我认为终止程序是唯一理性的选择----体面点说,叫做优雅地退出(在我看来,许多场合abort足够优雅)。不那么可靠的办法是,禁止所有和错误点关联的功能。这个有点难,但是不那么刺耳。只是我想说的是,你真有代码能预防脑子抽筋儿干蠢事吗?
工程上,对于错误,我们能做的,一是尽可能多,尽可能早地发现,二是一旦发现,就是尽量保留现场做尸检。

什么是异常?我当初是看到这样的定义,才真正理解异常的:
当前条件满足,后条件无法达成的时候,抛出异常。

什么情况下前条件满足,后条件无法达到?这同样是设计时的取舍。比如读取文件,如果前条件是“文件名合法”,后条件是“读出整个文件内容并返回“,那么在读取内容过程中如果失败(假设有这样几种失败可能:打开文件失败,系统 API 临时性错误,内存不足等),都可以作为异常抛出。但是如果后条件是:1)打开文件失败则返回错误;2)系统 API 临时性错误则返回错误;3) 内存不足则返回错误;4) 成功读取返回文件内容。后条件还会不满足么?
 
1.确实是设计时候的取舍。但是一旦你设计好,那么这就是无歧义的。

2.你这个前条件歧义太严重。什么叫合法文件名,文件名称符合规范?文件确定存在?文件保证能打开?后条件也不太好,我不说是不对的。作为一个害怕损伤脑细胞的人来说,我希望在后条件说清楚caller要的是什么,或者说callee给的是什么(两者等价)。我还希望这个“什么”是一个能理解的概念,而不是一堆杂乱的约束。

3.我不想说会不满足,确实有一定能满足的后条件。比如内存校验错你就没提到。当然,我这么说是在顶牛了。但我想说的是,不是所有后条件都必须是代码能检测的,例如返回合法指针的“合法”检测。

4.最后,设计,包括前后条件的选择,不是为了让人脑残,而是为了让人理解。 
Reply all
Reply to author
Forward
0 new messages