答《再谈Delphi vs VC++》
---- 戳破别有用心的谎言
近日在CSDN读到《再谈Delphi vs VC++》,由蒋涛先生作序并荐之为"非常精彩"。吾人秉持Bjarne之观念,本不欲介入程序语言的优劣之争,但于不诚实的学问态度,却是不得不说几句的。
作为业界语言比较的习惯,作者当然的在开篇处即摆正了"不偏不倚"的姿态。然而事实,却一如既往的让人失望:学界人所共知的"六经注我",又一次的在IT界拉幕开演。"六经注我"在学界已是人人喊打的老鼠,但在软件业界,这种将原始材料任意歪曲、曲解以迎合自身观点的行为,却迎来彩声一片,让人吃惊。
言归正传,下面请朋友们和我一起看看作者的冶学方法上的疏失。
一:编译器
编译器,作为语言的程序现实,常常对语言本身的成功与否起着决定性作用。特别那些流行的编译器,往往会在人们的心中和语言自身等同起来,成为语言的代名词。自然的,《再》文用数据详细比较了Delphi、VC的编译器,毕竟,"borland的编译器是最好的"嘛。
然而,作者详尽的数据真的可信吗?让我们来看看作者对代码生成长度的的说法:
"其实delphi/c++ builder不论在动态连接或静态连接的情况下,生成的程序都要比vc++的小。比如mdi的例子程序:在delphi/c++ builder中选new ... | projects | mdi application,在vc++中用mdi app wizard;生成的程序功能是非常类似的。下面是比较结果:
(delphi打开优化,c++ builder使用最大速度优化,vc++ 5使用最小代码优化)
delphi 3 delphi 5 c++ builder 5 vc++ 5
dynamic link 21k 35k 44k 70k
static link 253k 398k 467k 490k"
笔者测试的结果又是如何呢??
VC6.0 代码最小优化:(无文档/视图支持)动态:24K
最大速度优化:(无文档/视图支持)动态:24K
代码最小优化:(无文档/视图支持)静态:256K
最大速度优化:(无文档/视图支持)静态:256K
代码最小优化:(有文档/视图支持)动态:28K
最大速度优化:(有文档/视图支持)动态:32K
代码最小优化:(有文档/视图支持)静态:288K
最大速度优化:(有文档/视图支持)静态:292K
可以看出,《再》文所列数据有失实之处。自然,会有朋友指出,这可能是VC5同VC6的版本差异。我没有VC5,但我可以告诉你,因着MFC库自身规模的影响,5.0版静态连接的代码长度,应该较6.0版为大。而动态连接,即使考虑优化技术的巨大进步,也当在同一个数量级上,断不会出现成倍缩减的情况。
再来看看《再》文浓墨重彩的代码生成质量。在这里作者给出了三种编译器的比较:
(1) object pascal:
procedure foo;
var
i, j: integer;
begin
for i := 0 to 15 do j := j + i;
end;
(2) c++
void foo(void)
{
int i, j;
for (i = 0; i < 16; i++) j = j + i;
}
delphi 3生成的代码(打开优化): 字节数 时钟周期
00424aa9 33c0 xor eax,eax 1
00424aab 40 inc eax 1
00424aac 83f810 cmp eax,0x10 1
00424aaf 75fa jnz -0x06 0 (可并行)
-----------------
8 3
c++ builder 5生成的代码(最大速度优化):
00401535 33c0 xor eax,eax 1
00401537 40 inc eax 1
00401538 83f810 cmp eax,0x10 1
0040153b 7cfa jl -0x06 0 (可并行)
-----------------
8 3
visual c++ 5生成的代码(最大速度优化):
27: for (i = 0; i < 16; i ++)
00401205 mov ecx,dowrd ptr [j] 1
00401208 xor eax,eax 0 (可并行)
28: {
29: j = j + i;
0040120a add ecx,eax 1
0040120c inc eax 1
0040120d cmp eax,10h 1
00401210 jl foo(0x0040120a)+0ah 0 (可并行)
00401212 mov dword ptr [j],ecx 0/1 (取决于上一条指令的分支预测情况)
30: };
-----------------
16 4.2 (假定分支预测准确度80%)
vc++ 5的最小代码优化生成也有11个字节:
27: for (i = 0; i < 16; i ++)
00401205 xor eax,eax 1
28: {
29: j = j + i;
00401207 add dword ptr [j],eax 1
0040120a inc eax 1
0040120b cmp eax,10h 1
0040121e jl foo(0x00401207)+7 0 (可并行)
30: };
-----------------
11 4
我不太熟悉汇编语言,但也可以看出,作者所列的Delphi生成的代码,当比VC有33%以上的速度优势。然而,测试对等吗?从文中所列汇编语句可以看出,在代码生成时,VC编译器处于Debug模式。众所周知,Debug时需保证语言与生成代码呈现对应关系,故大量的优化选项将不起作用。因个人能力关系,我不能给出Release模式下的汇编代码,希望熟悉汇编的朋友能予以补成。
至于jake's code efficiency challenge,说心里话,我是绝不相信Delphi站点对Delphi的评测的,正如我不会相信VC站点对VC的评测一样,你呢?
二:语言特性
无疑,语言特征是程序语言的本质所在。选择某种语言,抑或不选某种语言,语言有否着你需用的特征,可能是最强有力的理由了。《再》文在下述几个方面进行了比较:
(1) 预处理,宏以及.h文件
先看看《再》文的说法:
"object pascal不支持预处理,其实是不需要。无法直接编译源代码的编译器才需要预处理器的支持"
"至于macro和.h则应该说是垃圾特性,只是由于兼容性的考虑才保留下来的。ansi/iso c/c++规范中明确建议:“不要使用macro和.h,应该使用程序中的常量定义和函数替代”。"
这是真的,预处理的确是过时的特性,Effective C++中第一条即是:"Prefer const and inline to #define"。但对.h文件我持保留态度,毕竟,将声明和实现分开,是一种更有弹性的程序设计风格。
(2) 集合,子界类型
"c++不支持这些object pascal的原生类型(编译器能直接识别的)。但可以用类来模拟,毕竟c++的对象结构是最复杂灵活的(ada除外)。不过性能有损失。"
(3) 枚举类型
"object pascal不支持为每个枚举元素指定值。"
(4) 数组
"c/c++不支持object pascal中指定下标的数组,下标只能从0开始。"
(5) 结构
"object pascal不支持struct"
(6) 字符串
"字符串处理是object pascal的强项之一"
"c++中的string/ansistring是用类模拟的,所以性能..."
上举(2)(3)(4)(5)(6)都涉及数据类型问题。众所周知,数据类型的丰富与否,会在很大程度上决定语言的语意表达能力。C++的原生类型不及object pascal丰富,这是无庸置疑的。但正如作者所言,C++有着最灵活的次生类型机制,故语意表达能力并未因此受到限制。
文中(5)指出"object pascal不支持struct"。实际上,在C++中,除缺省存取权限不同外,struct与Class完全相同。故应视作object pascal中存在的语言成份。(2)(6)提出了效率问题,可以看出《再》文作者并未了解由STL所提供的、与原生类型效率相近的解决方案,更未能理解C++语言"仅将核心放入语言,余者用类库实现"的设计理念。自然会有朋友问,贯穿如此理念的C++会有如此复杂的设计吗?嗯,那是出于"与C完全兼容"及"面向对象与效率结合"两条更高优先级理念的缘故。
(7) 多重继承
"毫无疑问,object pascal不支持多重继承;并且也看不出borland有增加这一特性的意向(其实增加是轻而易举的)。object pascal通过接口(interface)实现多重继承。"
"你真的需要多重继承吗?我想大多数程序员和我一样都从来没有使用过多重继承(连vcl这么强大灵活的体系结构都根本没有用到多重继承)。"
同样是Effective C++中有着对多重继承的中肯评价:"Depending on who's doing the talking, multiple inheritance (MI) is either the product of divine inspiration or the manifest work of the devil. Proponents hail it as essential to the natural modeling of real-world problems, while critics argue that it is slow, difficult to implement, and no more powerful than single inheritance. (据来自各个人群的不同说法,多重继承(MI)不是神圣灵感的产物,就是恶魔巫术的结晶。拥护者为其提供的'对现实世界自然而本质的建模方式'大声欢呼;而批评者则指出它太慢,难于实现,并没有比单一继承更强大的地方。)"
(8) 对象模板
"object pascal不支持对象模板。因为对象模板不过是宏的语言实现而已(宏本身不是c/c++的语言特性)。"
这就是《再》文作者对模板机制的唯一评述。在我的概念中,C++中强有力的模板机制,不会只值这寥寥几字的。C++支持四种程序设计模式:(1)过程程序设计(2)基于对象程序设计(与Ada相似)(3)面向对象程序设计(4)泛型程序设计。其中的泛型程序设计,就是在模板机制的基础上实现的,并在STL中得到了大量的使用。
本质上,这关系到语言的语意表达能力。一种拥有模板机制的语言,所能表达的,自然多于未提供此种机制的语言。特别的,当其与运行时多态相结合时,可以表达出纯粹面向对象难以表达的语意。UML中所谓的参数类,其实质即为模板类的抽象。
再看看Java,业界领先的纯面向对象语言。虽然Bruce Eckel在他著名的《think in java》中表示不认同模板机制,但下个版本的Java,也将会拥有类似模板的泛型机制。
顺带指出《再》文错误:C++中模板分为类模板和函数模板,并无"对象模板"的提法。
(9) 重载
"object pascal支持函数/过程的重载,不支持运算符重载。c++全部支持。(ps:我个人倾向于object pascal应该增加对运算符重载的支持)"
(10) 位及逻辑操作
"object pascal和c/c++在这方面没什么差别。"
(11) 风格
"其实这是我更倾向于使用delphi的一个重要原因(由于工作的原因,我也经常使用c++和汇编)。就象有些文章所说的:“object pascal和c++是同一重量级的语言”,确实难分轩轾,差别反而主要是在风格上。c++强调灵活,而object pascal更注重整洁和优美。"
"object pascal和c++是同一重量级的语言"?这实在是传播很广的误解。自C++出现以来,即得到业界的广泛支持。在成为ANSI/ISO双重标准后,更为各种软硬件平台所支持。不可否认,pascal语言亦得到着广泛使用,更成为
Macintosh上的主流开发语言。但请注意,是pascal,而非object pascal。后者仅为有限公司在有限平台上支持,实在说不上同C++在一个重量级上。
九六年时读到过Java和C++特性对比,有句话印象很深:"用C++干活,如同使用卸去护壳的电锯,有力但不安全。"嗯,不错,使用C++须有更高的纪律性,这是C++"给程序员更多控制"的设计理念的代价。
语言的学习,我常视作三个相续的阶段:
(1)语法语意的学习。语法如何使用?指定的语意如何表达?
(2)内部实现的学习。编译器如何实现?指定的语言成份内部如何表达?
(3)设计理念的体会。当你步入此一阶段时,应当会发现,每一种的语言都有其自身的设计理念。为什么C++有多重继承更Delphi没有?为什么Java内嵌垃圾收集器,而C++却作为第三方类库提供?从语言理念的考量与平衡出发,你应当能一一给出答案。
3. 功能及其他
前面比较的,是语言的两个基本要素:语言本身和编译器。二十年前这就是一切了,程序员总得从头展开,在可怜的函数库上层层堆叠自己的代码。但framework的引入带来了革命。"我比别人看的远,只因为我站在巨人的肩上",牛顿的这句话,正可形容framework在开发中的地位。
革命的另一主角即是所谓的IDE(集成开发环境)。老辈的程序员让手指在键盘上欢腾跳跃,让屏幕有着眼花缭乱的翻卷切换,嗯,一场键盘上的"手指之舞"。不过这是鼠标起舞的时代了,开发工具之争,重点已自语言和编译器转向framework与IDE的结合上了。哦,不要跟我提Java,它下一步同C#的竞争,重点也当是放在背后的framework之上的。
(1) 易用性
"毫无疑问delphi有巨大优势,这不用多说了吧。"
(2) 适用范围
"vc++几乎能做任何硬件允许的工作。delphi也能。"
自开发者的角度考察,delphi极强的易用性主要来自两个方面:
(1)RAD开发方式。RAD是最直观的开发,"鼠标之舞"不知释放了多少程序员的开发能力。但天下没有白吃的午餐,受代码零碎、增量开发易于粒度太小等影响,完全使用RAD开发大型系统始终是对软件工程能力的一个挑战。
(2)高层次的封装。众所周知,与MFC很薄的封装层相较,VCL的封装层次很高,是故通过VCL,程序员不须接触
底层细节,因而提高了易用性。这并不意味着Delphi失去对底层操作的能力:设计合理的framework总会提供击穿代理层的方法。但击穿代理层毕竟是非常态,效率及方便性往往就不在设计者考量之内了。
(3) 集成开发环境
(4) 数据库支持
(5) 网络功能
(6) 组件支持
"delphi除了基于object pascal的vcl/clx外,也支持基于com/dcom的组件(比如activex),另加corba支持。vc++只支持支持基于com/dcom的组件。"
VC++开发corba程序时,必须使用第三方提供的开发库。
(7) 应用框架/设计思想
"vcl比mfc至少领先一代,这也毋须多言。"
VCL领先于MFC,这是不容置疑的事实。对framework的评价,除了《再》文所指对底层API的封装,更重要的,该是对消息机制的封装。消息如何在对象间传递,实是framework之核心,对整个框架的对象模型有决定性的影响。VCL之优于MFC,实质就是"责任链机制"优于MFC宏实现的"消息映射机制"。至于底层API的封装,你我高兴起来,也是能按自已心意实现一个的。
(8) 调试
(9) 运行环境/系统需求
(10) 产品质量/稳定性
"有文章称“vc++的质量好,稳定性高”。真的是这样吗?visual studio的service pack不是都出到4了吗?什么是service pack?主要不就是bug fix + patch吗?!"
"实说到底,程序质量好不好,运行稳定不稳定,主要取决于开发者的水平/责任心。"
卡尔.波普在他的科学哲学中提出:命题只能被证伪,不能被证实。引申到软件工程,软件只能被证明有bug,不能证明不存在bug。事实上,如此规模的软件,不可避免的有着大量潜藏的错误。连续的service pack正说明测试除错工作在不间断的进行着,较诸置之不理的驼鸟政策有责任心多了。
抛开类库及工具本身的质量不谈,语言的选择,软件质量也是有影响的。在"稳定至上"理念指导下的语言,普遍会提供强力的容错机制,同时对语言本身的设计,也进行了防止程序员犯错的考量。在Java取消指针,正是贯彻了"稳定至上"的理念。
(11) 帮助/文档
(12) 国际化支持
(13) 应用领域
"vc++在windows设备驱动开发(毕竟是m$ windows)和某些桌面应用(比如游戏)开发中用得较多。delphi更多应用在数据库/多层结构,多媒体和internet开发等方面。"
其实使用VC++的大户是通信领域。就我个人而言,常将VC定位于通用软件的开发,需要尽力做到少而精的那种;而Delphi则用于客户定制软件的开发(纯个人看法)。
(14) 价格
(15) 向下兼容性
这是《再》文避免谈及的问题。事实上,Delphi在此方面作的不好。
(16) 前景
"不要几年,两个全都消失"。当然,这只是笔者激动时私下的嘀咕。
首先看看Borland的市场策略。事实上,Delphi正巧处在市场的夹缝中。不,并非常说的低端的VB和高端的VC,而是强有力的Java和新生的C#。刚巧,二者都支持RAD开发方式、拥有强力的网络特性、对数据库支持作了优化,Delphi何处容身?Borland公司以Kylix作出回答:
"提供能发挥平台特征的跨平台移植性"。Java提供平台无关性,C#则更能发挥平台特性;翻过来看,则Java不能发挥平台特性,C#没有跨平台移植性。两者间的市场空间,尽够Delphi苟延残喘了。
VC++会走向何方呢?不可避免的,VC++的作用将向后台偏移,走出前台桌面->后台服务->底层支撑->系统开发 样一条道路。这,就是重视效率的C++的宿命。
4. 结论
"delphi(其实应该说borland产品)在技术上有优势,vc++(其实应该说m$产品)也占有相当的市场份额。"
这实是个没有结论的结论。嗯,所有的答案,不都写在前文的分析与说明中了吗?
《再》文的附5就不分析了,毕竟,只是情感的廉价发泻而已。
我不讳言我是VC的使用者,对C++有特殊的感情。但我希望,对《再》文的评述,没有矫枉过正的缺失;希望日后读到的文章,都能直面事实,不再有"六经注我"的怪病出现。
Ps:再谈Delphi vs VC++ http://www.csdn.net/develop/read_article.asp?id=5264
Touch me: dream_soft@263.net