首页 诗词 字典 板报 句子 名言 友答 励志 学校 网站地图
当前位置: 首页 > 教程频道 > 开发语言 > C++ >

被误解的C++——软件工程解决办法

2012-02-27 
被误解的C++——软件工程被误解的C++传统上认为,C++相对于目前一些新潮的语言,如Java、C#,优势在于程序的运行

被误解的C++——软件工程
被误解的C++
传统上认为,C++相对于目前一些新潮的语言,如Java、C#,优势在于程序的运行性能。这种观念并不完全。如果一个人深信这一点,那么说明他并没有充分了解和理解C++和那个某某语言。同时,持有这种观念的人,通常也是受到了某种误导(罪魁祸首当然就是那些财大气粗的公司)。对于这些公司而言,他们隐藏了C++同某某语言间的核心差别,而把现在多数程序员不太关心的差别,也就是性能,加以强化。因为随着cpu性能的快速提升,性能问题已不为人们所关心。这叫“李代桃僵”。很多涉世不深的程序员,也就相信了他们。于是,大公司们的阴谋也就得逞了。
这个文章系列里,我将竭尽所能,利用一些现实的案例,来戳破这种谎言,还世道一个清白。但愿我的努力不会白费。


软件工程

一般认为,使用Java或C#的开发成本比C++低。但是,如果你能够充分分析C++和这些语言的差别,会发现这句话的成立是有条件的。这个条件就是:软件规模和复杂度都比较小。如果不超过3万行有效代码(不包括生成器产生的代码),这句话基本上还能成立。否则,随着代码量和复杂度的增加,C++的优势将会越来越明显。
造成这种差别的就是C++的软件工程性。在Java和C#大谈软件工程的时候,C++实际上已经悄悄地将软件工程性提升到一个前所未有的高度。这一点被多数人忽视,并且被大公司竭力掩盖。
语言在软件工程上的好坏,依赖于语言的抽象能力。从面向过程到面向对象,语言的抽象能力有了一个质的飞跃。但在实践中,人们发现面向对象无法解决所有软件工程中的问题。于是,精英们逐步引入、并拓展泛型编程,解决更高层次的软件工程问题。(实际上,面向对象和泛型编程的起源都可以追溯到1967年,但由于泛型编程更抽象,所以应用远远落后于面向对象)。
一个偶然的机会,我突发奇想,试图将货币强类型化,使得货币类型可以采用普通的算术表达式计算,而无需关心汇率换算的问题。具体的内容我已经写成文章,放在blog里:http://blog.csdn.net/longshanks/archive/2007/05/30/1631391.aspx。(CSDN的论坛似乎对大文章有些消化不良)。下面我只是简单地描述一下问题,重点还在探讨语言能力间的差异。
当时我面临的问题是:假设有四种货币:RMB、USD、UKP、JPD。我希望能够这样计算他们:
RMB   rmb_(1000);
USD   usd_;
UKP   ukp_;
JPD   jpd_(2000);

usd_=rmb_;//赋值操作,隐含了汇率转换。usd_实际值应该是1000/7.68=130.21
rmb_=rmb_*2.5;//单价乘上数量。
ukp_=usd_*3.7;//单价乘上数量,赋值给英镑。隐含汇率转换。
double   n=jpd_/(usd_-ukp_);//利用差价计算数量。三种货币参与,隐含汇率转换。
而传统上,我们通常用一个double或者currency类型表示所有货币。于是,当不同币种参与运算时,必须进行显式的汇率转换:
double   rmb_(100),   usd_(0),   ukp_(0),   jpn_(2000);

usd_=rmb_*usd_rmb_rate;
ukp_=(usd_*usd_ukp_rate)*3.7;
double   n=jpd_/((usd_*usd_jpd_rate)-(ukp_*ukp_jpd_rate))
很显然,强类型化后,代码简洁的多。并且可以利用重载或特化,直接给出与货币相关的辅助信息,如货币符号等(这点我没有做,但加上也不复杂)。
在C++中,我利用模板、操作符重载,以及操作符函数模板等技术,很快开发出这个货币体系:
template <int   CurrType>
class   Currency
{
public:
      Currency <CurrType> &   operator=(count   Currency <ct2> &   v)   {

      }
public:
      double   _val;

};
template <int   ty,   int   tp>
inline   bool   operator==(currency <ty> &   c1,   const   currency <tp> &   c2)   {

}
 
template <int   ty,   int   tp>
inline   currency <ty> &   operator+=(currency <ty> &   c1,   const   currency <tp> &   c2)   {

}
template <int   ty,   int   tp>
inline   currency <ty>   operator+(currency <ty> &   c1,   const   currency <tp> &   c2)   {

}

总共不超过200行代码。(当然,一个工业强度的货币体系,需要更多的辅助类、函数等等。但基本上不会超过500行代码)。如果我需要一种货币,就先为其指定一个int类型的常量值,然后typedef一下即可:
const   int   CT_RMB=0;//也可以用enum
typedef   Currency <CT_RMB> RMB;
const   int   CT_USD=1;
typedef   Currency <CT_USD> USD;
const   int   CT_UKP=2;
typedef   Currency <CT_USD> USD;
const   int   CT_JPD=3;
typedef   Currency <CT_USD> USD;

每新增一种货币,只需定义一个值,然后typedef即可。而对于核心的Currency <> 和操作符重载,无需做丁点改动。
之后,我试图将这个货币体系的代码移植到C#中去。根据试验的结果,我也写了一篇文章(也放在blog里:http://blog.csdn.net/longshanks/archive/2007/05/30/1631476.aspx)。我和一个同事(他是使用C#开发的,对其更熟悉),用了大半个上午,终于完成了这项工作。
令人丧气的事,上来就碰了个钉子:C#不支持=的重载。于是只能用asign <> ()泛型函数代替。之后,由于C#的泛型不支持非类型泛型参数,即上面C++代码中的int   CurrType模板参数的泛型对等物,以及C#不支持泛型操作符重载,整个货币系统从泛型编程模式退化成了面向对象模式。当然,在我们坚持不懈的努力下,最后终于实现了和C++中一样的代码效果(除了那个赋值操作):
assign(rmb_,   ukp_);
assign(usd_,   rmb_*3.7);

我知道,有些人会说,既然OOP可以做到,何必用GP呢?GP太复杂了。这里,我已经为这些人准备了一组统计数据:在C#代码中,我实现了3个货币,结果定义了4个类(一个基类,三个货币类);重载30个算术操作符(和C++一样,实现10个操作符,每个类都得把10个操作符重载一遍);6个类型转换操作符(从两种货币类到第三货币类的转换操作符)。


这还不是最糟的。当我增加一个货币,货币数变成4个后,数据变成了:5个类;40个算术操作符重载;12个类型转换操作符重载。
当货币数增加到10个后:11个类;100个算术操作符重载;90个类型转换操作符重载。
反观C++的实现,3个货币时:1个类模板;1个赋值操作符重载模板;10个算术操作符重载模板;外加3个const   int定义,3个typedef。
10个货币时:1个类模板;1个赋值操作符重载模板;10个算术操作符重载模板;const   int定义和typedef分别增加到10个。
也就是说C++版本的代码随着货币的增加,仅线性增加。而且代码行增加的系数仅是2。请注意,是代码行!不是类、函数,也不是操作符的数量。而C#版本的代码量则会以几何级数增加。几何级数!!!
这些数字的含义,我就不用多说了吧。无论是代码的数量、可维护性、可扩展性C++都远远好于C#版本。更不用说可用性了(那个assign函数用起来有多难看)。
我知道,有些人还会说:货币太特殊了,在实践中这种情况毕竟少见。没错,货币是比较特殊,但是并没有特殊到独此一家的程度。我曾经做了一个读取脚本中的图形信息,并绘图输出的简单案例,以展示OOP的一些基本概念,用于培训。但如果将其细化,可以开发出一个很不错的脚本绘图引擎。其中,我使用了组合递归、多态和动态链接,以及类工厂等技术。就是那个类工厂,由于我使用了模板,使得类工厂部分的代码减少了2/3,而且没有重复代码,更易维护。关于抽象类工厂的GP优化,Alexandrescu在其《Modren   C++   design》中,有更多的案例。同样的技术,还可以推广到业务模型的类系统中,优化类工厂的代码。
如果还不满意,那么就去看看boost。boost的很多库实现了几乎不可想象的功能,比如lambda表达式、BGL的命名参数等等。它为我们很多优化软件代码新思路,很多技术和方法可以促进我们大幅优化代码,降低开发成本。
最后,如果你认为C#的最大的优势在于.net平台,那我可以告诉你,这个世界上还有一种东西叫C++/CLI,完全可以满足.net的开发,而且更好,足以擦干净.net那肮脏的屁股。不过,这将会是另外一个故事了…


[解决办法]
to zhu_wj:
我没有抨击楼主啊,我就是想起来这些事情而已……
[解决办法]
支持!支持!!支持!!!
[解决办法]
楼主首先就错得没边了,
软件工程追求的是软件开发过程的标准化和可预测性、可重复性,
在软件工程中语言特性之类的属于细枝末节。

[解决办法]
to: jacklondon(jacklondon

我觉得Java 与 .Net 最大的好处在于内存自动回收。C++ 内存问题一直很严重

///////////////////////////////
C++程序中的内存问题,不要指责C++语言,C++语言没有任何责任,内存问题是程序员导致的,也是完全可以避免的,不要指望语言给你做一切事情,程序员是一个创造者,而不是一个"遗产继承人",.
  你只是看到Java和.Net的内存自动回收的好处(当然,如果真的能算得上是好处),但是你有没有看到所谓的内存收集的代价--放弃程序运行的效率,不要告诉我现在的机器都是1G,双核,256独立显卡,程序的运行效率已变得不重要了,现在的硬件配置是好了,但是你要记住,用户的硬件配置不是只为你一个软件服务的!你再想一下,如果"魔兽"不是用C++写的(当然,还用了一些专用的游戏脚本语言),而是用Java或C#写的,你说"魔兽"会这么火爆吗?不会,一定不会.原因很简单,你可以想象一下,有几台机器上可以跑得动"魔兽",即使是跑动了,你认为玩家会接受"砍一刀之后,过几秒,小强才倒下"的速度吗?如果你真的说你能接受,那我无语了!
  总是有很多人在说"指针"有多么多么不好,但是那些人看到过指针带来的灵活性没有,看到指针产生的效率没有.举个简单的例子,你去看看老外写的那些经典的"数据结构和算法"的书里面,有几种数据结构能离开指针而高效简洁的实现,
  很多人都说C++的开发效率很低,但事实是这样的吗,不一定,仔细想想,其它的所谓快速开发语言,之所以快速很大程度上得益于它所提供的类库,所以很多时侯,很多功能你都可以不用自己实现,直接从类库中抓出一把功能装进程序里面去,OK,然后功能就搞定了,我不否认这是一件好事,毕竟,我们没有"重复发明轮子"的必要.但是,C++就没有这种快速开发的功能吗,首先,泛型编程,就能极大的提高程序的开发效率,当然有人会说,Java,C#里也有泛型编程这么回事,但是,那种泛型只能用"搞笑"两个字形容.
  另一方面,有多少人听说过ACE,Qt,Boost...这些功能强大的库,如果你用上这些功能强大的库,难道开发不会更快一些吗,ACE,Boost库都是开源库,Qt有GPL下的开源库,当然Qt也有商业版的.如果C++也如某些语言那般喜欢做秀,C++完全可以把ACE,Qt,Boost库加进去,不会有任何版权问题,但C++没有这么做,当有人问及B.S大师,为什么C++0X(极有可能是C++09标准)标准里面不加进一个UI类库,大师说,他不希望C++语言变得那么臃肿,还有很多很好的第三方UI库.
总之,C++就是一种"极好的工具,富于艺术的创造性,独立于任何的商业吵作和商业纷争之外,全心全意为每一个使用C++做开发者服务",至于你要用C++做什么,你能用C++做什么,你自己看着办吧!
C++0X标准,我们拭目以待,同志们,加油!!!!


热点排行