Chinaunix首页 | 论坛 | 博客
  • 博客访问: 19271601
  • 博文数量: 7460
  • 博客积分: 10434
  • 博客等级: 上将
  • 技术积分: 78178
  • 用 户 组: 普通用户
  • 注册时间: 2008-03-02 22:54
文章分类

全部博文(7460)

文章存档

2011年(1)

2009年(669)

2008年(6790)

分类: C/C++

2008-05-31 09:21:54

最近比较闲,就看了那本放在书架上很久的“砖头”著作--经典的《CODE COMPLETE (Second Edition)》。当然是中文版的,这种大块头的书看英文版是非常累的。
    看到第18章表驱动法Table-Driven-Methods时,感觉这种方法非常好,而我以前也从来没用过这种方法。于是就多看了两眼,没想到就是这多看的两眼,竟然让我在在C++示例代码中发现了大师级作者Steve McConnell犯的2个低级错误!
    在18.2直接访问表Direct Access Tables的例子:灵活的消息格式Flexible-Message-Format Example小节中,有下面这几段代码:
// 第1段(P420)
enum FieldType {
   FieldType_FloatingPoint,
   FieldType_Integer,
   FieldType_String,
   FieldType_TimeOfDay,
   FieldType_Boolean,
   FieldType_BitField,
   FieldType_Last = FieldType_BitField
};

// 第2段(P422)
AbstractField* field[ Field_Last ];

// 第3段(P422)
field[FieldType_FloatingPoint ] = new FloatingPointField();
field[ FieldType_Integer ] = new IntegerField();
field[ FieldType_String ] = new StringField();
field[ FieldType_TimeOfDay ] = new TimeOfDayField();
field[ FieldType_Boolean ] = new BooleanField();
field[ FieldType_BitField ] = new BitFieldField();

// 第4段(P423)
fieldIdx = 1;
while ( ( fieldIdx <= numFieldsInMessages ) && ( fieldStatus == OK ) ) {
   fieldType = fieldDescription[ fieldIdx ] .FieldType;
   fieldname = fieldDescription[ fieldIdx ].FieldName;
   field[ fieldType ].ReadAndPrint( fileName, fileStatus);
}
    我们先来看第1段代码。在C++中,枚举类型的值在默认情况下是从0开始的。所以,代码中的FieldType_FloatingPoint值为0,而FieldType_BitField的值为5。那么FieldType_Last的值也是5。
    再看第2段代码。从上面的分析可以得出,第2段代码跟下面的代码是等价的:
[NextPage]

AbstractField* field[ 5 ];
    这样的话,第3段代码就会错误!因为,在第3段代码中,向一个只有5个元素空间的数组中放置了6个元素。这是明显的下标越界。一个非常低级的错误!
    其实,要想改正这个错误是非常简单的,只要把第1段代码改成下面的样子就行了:
enum FieldType {
   FieldType_FloatingPoint,
   FieldType_Integer,
   FieldType_String,
   FieldType_TimeOfDay,
   FieldType_Boolean,
   FieldType_BitField,
   FieldType_Last
};
[Page]
    我们再来看第4段代码。这段代码里面的错误就更加低级了。数组field中存放的都是指向对象的指针,而通过指针访问成员函数的话应该是用“->”而不是“.”,这是任何一个学过C++的人都知道的。可是我们的大师就犯了这么一个低级错误!正确的代码应该是:
fieldIdx = 1;
while ( ( fieldIdx <= numFieldsInMessages ) && ( fieldStatus == OK ) ) {
   fieldType = fieldDescription[ fieldIdx ] .FieldType;
   fieldname = fieldDescription[ fieldIdx ].FieldName;
   field[ fieldType ]->ReadAndPrint( fileName, fileStatus);
}
    由此可见,就算像Steve McConnell这样的大师级人物也会犯这种非常低级的错误的。
    不过,瑕不掩玉。这一章讲的表驱动法还是非常有用的,而且书中的例子也很好的说明了表驱动法在面向对象设计中优点。另外,总体来看,这也是一本好书。作者把在编程时应该注意的方方面面都进行了叙述,确实是一本很好的软件构建方面的指导书。
    (这里我想多说两句题外话--为什么这么简单的低级错误,译者却没有发现呢?我想可能是对权威的过度迷信吧。也许译者在翻译这本大部头的时候,根本没有去看书中所附的代码。大师也是人,当然也会犯错误。就像编写软件一样,肯定会有bug的,更何况这是一本关于软件构建的书,出现错误也在所难免。在这点上,我觉得内地的译者们应该向台湾的候捷学习。看他翻译的《Inside The C++ Ojecet Model》(深度探索C++对象模型)一书就知道了。他把在翻译时发现的所有错误都列了出来,并且在书中出现错误的地方改正过来,并加以注释。)
阅读(357) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~