KeyFC欢迎致辞,点击播放
资源、介绍、历史、Q群等新人必读
KeyFC 社区总索引
如果你找到这个笔记本,请把它邮寄给我们的回忆
KeyFC 漂流瓶传递活动 Since 2011
 

[M] Prelude to K.O. (4.5) + 12楼(4.75)

[ 15426 查看 / 31 回复 ]

回复:[M] Prelude to K.O. (4.5)

异常这东西我在写实际的程序中基本不用.因为既不使用抛出异常的库,自己也不需要抛出异常.
可能比较有用的异常是操作系统发出的,那个异常不是跨平台的,因此VC自己定义了一套__try/__except关键字来处理,这样就可以防止程序写无效指针等操作导致的立即退出.我不知道delphi如何做到这点的.
非console的大型工程一般都是写log文件的.log类只要做一下临界区处理就可以了.
TOP

回复:[M] Prelude to K.O. (4.5)

Delphi对异常处理有着完全不同的理念——既可以当成调试功能,又可以当成开发功能,甚至成为设计的一部分。(因此支持得很到位)
Delphi中所有操作系统的异常都被转换为Delphi的异常,因此对于Delphi编程者来说,异常是跨平台的(因为Linux的Signal也被包裹成统一界面的异常对象)。
飛べない翼に、意味はあるんでしょうか?
TOP

[M] Prelude to K.O. (4.75)

很久没有写K.O.这边的东西了。尽管以前的进度都快忘了,刚才打开工程组,稍稍浏览了一下各个模块的消息定义,立刻就可以开始往下面继续工作了。
看来CAS的重要目的之一似乎达到了期望的效果:低损耗暂停、恢复工程进度。

K.O.距离Alpha版本还有一小段时间,不过既然前面已经铺垫好了CAS架构,那么现在就可以方便的介绍一下K.O.汉化工具的大体雏形了。
K.O.使用运行级别为1的CAS"基本消息总线",程序结构大体上符合Prelude to K.O. (4)中介绍的"应用程序"型。

---
主程序结构(构建中):

因为整个程序(包括主程序)都采用模块化设计,因此内含两个虚拟模块:

UIService 位于 "KOLoaderMain" 中,负责处理图形界面的输入和反馈;
Controller 位于 "KOLocalizerController" 中,负责将用户的请求分解成各种操作,并分发到各个模块,最后收集结果并反馈给 UIService 模块。
同时 "KOLocalizerController" 中包含了程序所有模块的加载/卸载操作过程(由主线程于开始/结束时执行)。

Controller与各个模块的交互仅仅通过消息,因此需要引用各个 "_______MSG" 用于生成相应模块能够"明白"的服务请求。
(因此,目前这个工程的大体进度也暗示在这张图中了。)

---
消息总线模块: 内部结构图已经在贴顶介绍过了。

---
脚本包支持模块(完成):



用于将流形式存在的脚本包分解成为各个独立的(未解码)脚本流、以及反向操作。

---
脚本编码/解码模块(50%):



用于编码/解码独立的脚本流。(解码部分完成,编码部分还没有从旧项目中移植过来...-v-)

---
数据库支持(刚开始):



用于统一存放/管理脚本、原文以及翻译文字。

---
翻译导入/导出模块: 用于从数据库中提取翻译文字,储存为文本文件;以及反向操作。

---
Reallive指令识别: 用于准确,完整的从解码后的脚本中提取原文。

---
Reallive文本导入模块: 用于快速将翻译文字导入解码后的脚本中。

---
文字转码模块: 将翻译文字"安全"的翻译为Reallive引擎可以接受/外挂DLL可以解码的文字。

---
配置文件支持: 用于将必要文本导入信息写入外挂DLL的配置文件中。
最后编辑Prz 最后编辑于 2007-06-09 14:16:31
飛べない翼に、意味はあるんでしょうか?
TOP

回复:[M] Prelude to K.O. (4.5) + 12楼(4.75)

某毕业生只会C++和J2ME的飘过,最有成就的就是在毕业设计2个月中自己写了个3D的简单游戏。
决定以后往3D图形处理方面发展了。
我是宇宙中的一颗不显眼的小星星。对我来说,朋友们就像星空里的其他星星们那样的美丽且遥远。我总是想要触摸到他们,却总是够不到他们那遥远的身影。于是我开始感到悲伤,开始讨厌那广阔无边的宇宙。是它,把我们分开了!直到某一天,美丽的流星雨带来了朋友们的微笑。我才知道,原来自己是多么的傻。对啊,我们是朋友,无论有多么遥远的距离,我们永远是朋友。于是我又抬头看了看我的朋友们,看到的再也不是悲伤。因为他们都在自己努力着,他们都在向我一闪一闪地展示他们坚强的笑容...
TOP

回复:[M] Prelude to K.O. (4.5) + 12楼(4.75)

很明显的不良编程习惯

《effective c++》里有条铁则,绝对不要在析构函数里抛出异常=。=

程序的正常运行很多情况都是建立在,或者说逻辑上都需要析构函数不抛出异常
==================
PS:异常不是用来调试的,是将运行时错误处理与逻辑过程相分离

不知道delphi干嘛要把异常弄得那么复杂,异常处理又不是不要代价的

何况已经抛出异常了,在程序中捕获,打印异常链/栈就知道哪错了=。=

『智代after PS2汉化移植完毕』www.keakon.cn
TOP

回复:[M] Prelude to K.O. (4.5) + 12楼(4.75)

楼上估计不明白我前面说的"雪崩效应"。
C++中如果还没等捕获到异常的时候程序就和谐了,怎么打印?

另外,Delphi的异常复杂么?
飛べない翼に、意味はあるんでしょうか?
TOP

回复:[M] Prelude to K.O. (4.5) + 12楼(4.75)

析构函数不抛出异常就行了啊=。=

在里面catch是它的义务,不要把责任推给调用函数,那样增大了耦合度,也不利于封装性

从设计角度,没什么原因需要你析构时抛出异常;能想到的就是提示析构出错(比如关闭文件遇到IO错误)和纯粹为了恶搞

这种情况在析构函数内调用一个函数对象(事先自己注册和定义提示错误的方式),调用后返回就行了;更改那个函数对象,也可以做到更改显示方式;要避免重新编译,将其定义成接口,然后用不同方式实现该接口即可
================
复杂是说异常没必要帮助调试,感觉太过依赖这个不是什么好习惯=。=

『智代after PS2汉化移植完毕』www.keakon.cn
TOP

回复:[M] Prelude to K.O. (4.5) + 12楼(4.75)

析构函数不抛出异常就行了啊=。=

不仅如此,还必须在里面层层套用try...catch,头晕。

在里面catch是它的义务,不要把责任推给调用函数

出现异常,如果不是能够立刻被处理的,那就应该是交给调用者。
因为调用者处于整个软件命令链(Chain of Command)的高端,只有它才拥有足够的信息来作出正确的判断。
仅仅打印出来不叫处理,而且这才更像是在“帮助调试”,用您下面的话形容,“不是什么好习惯”。

那样增大了耦合度,也不利于封装性

整个工程采用统一异常对象,不仅利于封装,而且利于简化处理过程,减少代码出错的可能。

从设计角度,没什么原因需要你析构时抛出异常

从设计的角度,你很难保证析构时调用的所有函数都不抛出异常,不论什么原因。
当然,除非根本不用,这也是目前大中型C++工程的标准。

复杂是说异常没必要帮助调试,感觉太过依赖这个不是什么好习惯

似乎C++在析构时截获异常只能打印处理,然后丢弃,更像是在“帮助调试”。
CAS中将所有抛出的异常完整的保留并传向调用者,由调用者决策处理,这样才可能实现将异常融合于设计中。


------

哦,对了,似乎楼上最后的观点逆主流而行啊:(有可能是一激动说反了 =v=)
我的认识是:
异常作为调试功能是非常成熟而且有效的;但是因为异常的处理相对于返回值有较高的资源消耗,因此如果频繁的用于设计中,会降低程序运行的效率,“不是什么好习惯”。


这一点CAS已经考虑到了,因此绝大部分的异常都是为调试服务的,正常运行中的程序流程很少会抛出异常。
如果有将异常作为设计功能的函数,CAS一定同时提供了不抛出异常的版本,供程序设计者自由选择。
最后编辑Prz 最后编辑于 2007-06-11 13:01:59
飛べない翼に、意味はあるんでしょうか?
TOP

回复: [M] Prelude to K.O. (4.5) + 12楼(4.75)

不仅如此,还必须在里面层层套用try...catch,头晕。

C++是非常灵活的语言,适当利用智能指针可保证内存自动回收,这样就不用层层嵌套try...catch了.
如果使用带有引用计数的share_ptr或句柄模板类,那基本不需要手动delete任何对象了.
话说回来,delphi不也会面临层层套用try...catch的问题吗?

似乎C++在析构时截获异常只能打印处理,然后丢弃,更像是在“帮助调试”。

C++异常类可以设计成异常链的形式继续抛给上层.
不过构造异常类对象也有可以抛出异常,这样上层得到的异常对象要么不存在,要么描述异常的信息不完整,delphi也应该会遇到这种问题.

因此绝大部分的异常都是为调试服务的,正常运行中的程序流程很少会抛出异常。

异常主要不是用来调试的,调试不能因异常而停止后面代码的测试.
高效的调试方法应该是设置断点,assert,单步执行和查看变量.
异常是一种更方便快捷地处理无法继续运行的问题的机制.

从设计的角度,你很难保证析构时调用的所有函数都不抛出异常,不论什么原因。

析构确实没什么理由需要抛出异常,所有释放资源的函数都不需要抛出异常.
除了资源回收,析构还需要做什么呢?
我的原则是构造和析构都仅仅对成员变量做最基本的初始化和释放,不把复杂的工作放到构造和析构中.
如果真对析构中调用的函数不放心,就用cache(...)忽略掉或做个log好了.如果资源释放都不能如愿,对象析构也就没什么意义了.
最后编辑dwing 最后编辑于 2007-06-11 18:26:23
TOP

回复:[M] Prelude to K.O. (4.5) + 12楼(4.75)

C++是非常灵活的语言,适当利用智能指针可保证内存自动回收,这样就不用层层嵌套try...catch了.

据我有限的理解,所谓的智能指针仅仅是保证在对象不再被引用的时候自动“析构”而已,和异常处理风马牛不相及,不用try...catch一样会挂。
如有错误,请纠正。

话说回来,delphi不也会面临层层套用try...catch的问题吗?

估计你还是对Delphi的异常处理的能力没有概念,不过可以理解,就算一般的初等Delphi玩家也会有相同的错误概念。
不过看在您也算C++的经验玩家的份上,我就不点得太明白了:
*在双重throw的时候不会挂掉,就算不catch;任何使时候throw出去的exception都可以被接管,就算不catch* <--- 从这点开始发散您的思维,想象一下能够实现什么吧。

不过构造异常类对象也有可以抛出异常,这样上层得到的异常对象要么不存在,要么描述异常的信息不完整,delphi也应该会遇到这种问题

好像您有点晕了...异常处理不是从构造异常对象时开始的,而是从抛出那一刻开始的。
构造一个异常的时候,这个异常并没有抛出,对于机器而言,这个构造完全可能是正常的程序过程。这个时候如果发生异常,抛出的当然将是另一个的异常。
而且Delphi中永远不会出现构造不完整的对象 (解释就在您前面引用给我看得文章中),因此这种情况也不会出现C++中可能出现的内存漏洞问题。

异常主要不是用来调试的,调试不能因异常而停止后面代码的测试.
...异常是一种更方便快捷地处理无法继续运行的问题的机制.


这两句话明显自相矛盾:
如果程序遇到异常而可以不停止调试,那就证明程序可以继续运行下去,那么也就没有必要使用异常
(反映到实际使用中,比如分配、调用资源的失败,都可以用返回值,没有必要使用异常);
如果程序真的无法继续下去,那么抛出异常将是唯一的解决办法,而且是最好的调试方法,因为无法通过返回值处理
(反映到实际使用中,比如引用失效的指针、内存位置,没有有意义的返回值,有必要使用异常)。

我同意的说法是:
异常不是只能用来调试的,调试可以不因异常而停止后面代码的测试.
...异常是处理无法继续运行的问题的不可替代的机制.


析构确实没什么理由需要抛出异常,所有释放资源的函数都不需要抛出异常.
除了资源回收,析构还需要做什么呢?
我的原则是构造和析构都仅仅对成员变量做最基本的初始化和释放,不把复杂的工作放到构造和析构中.
如果真对析构中调用的函数不放心,就用cache(...)忽略掉或做个log好了.如果资源释放都不能如愿,对象析构也就没什么意义了.


您因为自己的习惯而引发的概论只能证明您的群体合作经验比较少。
我相信您的描述可以自圆其说,但是,你不能将自己的意愿强迫到别人的头上。别人怎么提供别人的实现,这是与您怎么实现您的对象是非常不同的事情。
Delphi和C++异常处理对于一个人的几万行程序来说,基本没有区别。但是对于多人,特别是以松散方式合作的组来说,C++异常的scalability要差得多(这里的意思是,随着规模的扩大而导致可用性的快速降低),这个是不争的事实。

当然如果你能强制别人按照你的思路来办事情,这样会好得多;
其实,如果强制所有人按一个人的思路来做事,世界上很多问题也都可以很好的解决。
但是问题是,无数的历史事实证明,独裁的结果肯定是快速灭亡,因此不是一个解决问题的办法。
最后编辑Prz 最后编辑于 2007-06-12 00:51:08
飛べない翼に、意味はあるんでしょうか?
TOP