文章标签 ‘C++’
很久以前看到过一篇解释HTML中DOM的Box Model的文章(好像是在MSDN,有图示,很清楚)。最近在写Windows程序,遇到了一个要resize一个window,并且要相应移动它里面的controls的需求。趁这个机会研究了一下Windows的Box Model,发现竟然和DOM的Box Model是如此的相似。 首先介绍一下与其相关的一些API:
在来到新公司之前,对于Windows编程实在不熟悉,更别说VC++中的Resource的实现了。工作的时候,同事提到了StringTable实现本地化的问题。当时我也很模糊,说不出个准确答案。周末回家研究了两天,算是搞明白了。 先说说我之前比较困惑的几个问题: 在编译完成的二进制文件中,StringTable到底是按照什么方式存储的,简单说,就是按照什么编码方式存储的? Windows已经支持了Unicode,为什么在VC++的StringTable Editor中,还要选择语言? 如果使用了Unicode,怎么有的时候,应用程序还会出现乱码? 如果是Unicode编码,那么是按照什么格式存储的,UTF-8,UTF-16? 当然,对于这些问题,可能对于一些牛人来说不算什么,都是很简单的问题。不过,对于我来说,还是很麻烦的,因为网上没有很直接解释这些问题的文章,最终还是自己一点一点研究出来的。 首先,需要复习和学习一些知识: 在计算机诞生的初期,操作系统只支持最多256个字符,就是著名的ASCII字符集。但是随着计算机在世界上各个国家的发展,本地化的需求越来越多,256个字符明显不够,就有一些国家发明了自己的字符集,也可以说是编码标准,不过这些编码方式都是只考虑自己国家的语言(当然不会有一个国家为了发展自己的科技,还要替别人着想),比如,ISO-8859-1,GB2312等等,在Windows里面,这类编码的体现就是我们熟悉的Code Page。在VC++中,Code Page是使用数字来代表的,比如:936是简体中文Code Page,1256是阿拉伯语言的Code Page。这样显然不爽,随着计算机的普及,Code Page会越来越多,并且很多Code Page可能相差不多,但是为了支持相应的用户,应用程序(包括操作系统)就不得不对之提供支持,这大大提高了软件的成本。终于,一些软件业的巨头决定做一件一劳永逸的事情,就是创造一个字符编码方式,能支持世界上所有的字符,这个字符集编码叫做Unicode。它设计可以支持11万多的字符,被认为足够放下世界上已知的所有字符(其中,中文字符占了2万多,英文只占52个)。而我们经常看到的UTF-8,UTF-16,UTF-32都是Unicode的一种存储方式。 言归正传,回答自己的四个问题: 从Windows2000开始,Windows已经完全支持了Unicode,所以StringTable在二进制文件(*.dll、*.exe)中确实是以Unicode编码存储的。 在StringTable Editor中,之所以要为StringTable选择相应的语言,是因为StringTable Editor中不是以Unicode存储的,而是基于Code Page编码存储的。所以必须对自己的StringTable选择正确的Code Page,否则系统将不能正确处理。因为在编译的时候,VC++的Resource Compiler会自动调用MultiByteToWideChar这个API,将字符串从Code Page编码方式转化为Unicode,如果Code Page选择不正确,那么转换也就必然不正确了。 出现乱码一个原因是操作系统不支持Unicode,比如Windows98之前的版本。或者是没有将默认编码设置为Unicode。 在Windows中都是使用UTF-16来存储Unicode的。UTF-16正好是两个字节,也就是我们使用的WCHAR类型。 需要说的是,StringTable Editor不是使用Unicode来存储,是因为历史原因。早期的VC++是不支持Unicode的。
看到了tangrui回应的文章,我想说的是,可能我们对于异常处理的理解有些地方是不一样的。 1. 我认为异常处理绝不是消息传递。这点在《Effective Java》中是明确说明的。因为如果简单地把Exception看成是另外一种返回值,代码会越来越混乱,也不符合Exception这个名词的含义。会让人看不懂,是非常不好的程序风格。(只能说是不好的程序风格,不是错误,因为编译器允许这样做) 2. 我认为RuntimeException和Checked Exception是有明显区别的。在Java中,所有非RuntimeException都是Checked Exception,而在C#中,完全没有区别。可以从字面上理解,RuntimeException是不可预知的异常,比如:NullPointerException(所有可预知的Null,都应该被避免了,剩下的都是不可预知的),BufferOverflowException(鬼知道什么时候会出现~),ArithmeticException(除数是零,永远不能是零的数,突然成了零),等等。而非RuntimeException,都是可以预知的。比如说,IOException(极有可能失败的操作),ClassNotFoundException(也是很有可能出现),InterruptedException(线程被中断,有可能),还有一切与应用逻辑相关的Exception等等。可以认为,如果发生了RuntimeException,那么这个异常必然不是预期可能出现的,都是不可恢复的。所有Checked Exception都是设计者可以预期的异常,大多数是可以恢复的。 3. 所有的RuntimeException都不应该被catch,而所有的Checked Exception都应该被catch。RuntimeException发生,说明程序有问题;Checked Exception发生,说明程序进入了一个可预知的意外情况。如果对于可预知的异常不作处理,那么这个问题是基本的coding问题,应该让编译器检查。 4. 一个接口所可能抛出的所有的Checked Exception都应该是接口设计的一部分。如果只有文档说明,这个风险太大了。很可能漏掉了很重要的异常问题没有处理。我希望可以由编译器来替我承担一部分风险。 所以,我并不是认为C#的方式非常的不好,只是认为,它为开发大型应用,带来了一定的风险。尤其是维护上的。而Java这种方式,非常严谨,在编译阶段为开发人员发现了很多有可能出现的潜在问题,虽然有可能会多写一些代码(其实这些代码是不多写的,无论用Java和C#,最终都是要写这些代码的)。至于tangrui说的,开发小程序可以快速上手的问题,我觉得不存在。因为如果真的是要成为优秀的开发人员,严谨的开发习惯是必须的,并不应该在乎异常处理所带来的麻烦。可以这么说,异常处理是开发过程中,非常重要的一部分。 这个问题我也就说到这里了,表达了我对异常处理的理解。我现在学习工作都要用C++,不能再像Java一样靠编译器了~hehe
刚看到tangrui同学的一篇文章,说Java与C#在异常处理上的区别。以前我们还是同事的时候,就一起讨论过这个问题,当时,我是极力的不满意C#的处理方式。这次看到这篇文章,有些给C#正名的意图,所以不得不说一说了。 首先还是声明一下,我不是大师,而设计Java与C#的都是大师,所以我并没有班门弄斧的意思,只是表达一下自己的观点和情感罢了。 对于Java和C#异常处理的区别,核心问题就是已知自定义类型的Exception是否应该在编译期检查。Java是强制检查,所有的接口定义都要声明自己要抛出哪些Exception,没有声明的绝对不能抛出(除RuntimeException外);C#是完全没有检查,任意接口都可以抛出任意类型的Exception。 通俗起见,我来举个例子:一把手枪,它有一个保险装置,在没有打开保险之前,扳机是不起作用的,当然,只要正确的将保险装置打开,就可以指哪打哪了;还有一把手枪,没有保险装置,但有一份很详细的说明书,告诉你这个东西有危险,有可能走火,小心点儿,以及如何正确使用并且避免走火,另外,作为附加,还给了你一块几平方分米防弹材料,建议你把它们放在走火的时候有可能打到的地方。那么,如果你是最终用户,你会选择哪把手枪呢?(需要说明的是,为了对比,我故意没有说,第一把手枪也有非常充分的说明书,但是很显然,不需要那不够用的防弹材料) 我觉得,Java就像是第一把手枪,C#像第二把手枪。 再从技术方面来说,几个问题: 1. 在设计一个接口的时候,是不是需要同时告诉用户,这个接口有可能抛出什么类型的Exception? 2. 如果需要,那么这个Exception类型是否应该成为一个接口声明的一部分? 3. 一个已知的有可能发生的exception被操作系统捕获是否可以说是用户在处理这个接口的声明的时候,出现了疏忽呢? 4. strong-type的目标是在编译器发现尽可能多的关于类型的错误,如果我们把接口的定义看作类型的一部分,那么是否可以说,这个有可能将checked exception暴露给操作系统的编译器在strong-type上有缺陷呢? 我的回答都是true。所以我觉得C#在strong-type上作的不够好。让用户不得不靠自己的记忆力以及本来就不太靠得住的细心来对付这些潜在的异常。 另外,我还有一个怀疑,就是C#是为了继承C++的异常处理方式,所以才放弃了Java的异常处理方式的。(只是怀疑,大家都知道,C++并不是strong type语言) 我对于编程语言并没有特别的偏好(也不允许有偏好,吃不饱的时候,就不挑食了),所以这里说的意思,只是说我觉得C#对于我这种懒得去背诵API的人来说,用起来不是很顺畅。








