《C++覆辙录》——1.9:使用糟糕的语言

1.9:使用糟糕的语言

当一个更大的世界入侵了C++社群原本悠然自得的乐土之时,它们带来了一些足堪天谴的语言和编码实践。本节乃是为了厘清返璞归真的C++语言所使用的正确适当、堪称典范之用语和行为。

用语
表1-1列出了最常见的用语错误,以及它们对应的正确形式。

表1-1 常见用语错误及其对应正确用语

没有什么所谓“纯虚基类”。纯虚函数是有的,而包含有或是未能改写(override)此种函数的类,我们并不叫它“纯虚基类”,而是叫它“抽象类”。

C++语言中是没有“方法”的。JavaSmalltalk里才有方法一说。当你颇带着一丝自命不凡地就面向对象的话题侃侃而谈之时,你可能使用像“消息”和“方法”这种用语。但如果你开始脚踏实地,开始讨论你的设计对应的C++实现时,最好还是使用“函数调用”或“成员函数”来表达。

还有一些不足为信的C++专家(是在说你吗?)使用“destructed”作为“constructed”的对应词。这明显是英语没学好35,正确的对应词是“destroyed”。

C++ 语言中确实有强制型别转换(或曰型别转换)运算符——事实上只有4个(static_castdynamic_castconst_cast以及reinterpret_cast)。遗憾的是,“强制型别转换运算符”常常被不正确地用于表达“成员型别转换运算符”,而后者指定了某种对象何以被隐式地转换到另外的型别。

class C {
  operator int *()const; // 成员型别转换运算符
  //...
};```
当然用强制转换运算符来完成型别转换的工作也是允许的,只要你不把用语搞混就成。

请参见常见错误31中有关“常量指针”和“指涉到常量的指针”的讨论,以加深对本主题的理解。

空指针
从前,当软件工程师使用预处理符号`NULL`来表示空指针时,他会遭遇潜在的灾难:

void doIt( char * );
void doIt( void * );
C *cp = NULL;```
麻烦出在NULL这个符号在不同的平台上,有很多种定义的方法:

#define NULL ((char *)0)
#define NULL ((void *)0)
#define NULL 0```
这些各扫门前雪的不同定义严重损害了C++语言的可移植性:

doIt( NULL ); // 平台相关抑或模棱两可?
C *cp = NULL; // 错误?``
事实上,在C++语言里是没有办法直接表示空指针的。但我们可以保证的是,数字字面常量0可以转换成任何一种指针型别对应的空指针。那也就是传统的C++语言保证可移植性和正确性的用法36。现在,C++标准规定像(void *)0这样的定义是不允许的37,可见这是个和
NULL的使用并无多大干系的技术问题(如若不然,NULL`岂不是成了格外受人青睐的预处理符号?其实它是普通不过的)。可是,真正领会了C++语言精神的软件工程师仍然使用字面常量038。任何其他用法都会使你显得相当非主流。

缩略词
C++软件工程师都有缩略词强迫症,不过与管理层相比,可谓小巫见大巫。表1-2在你的同事给你来上一句“RVO将不会应用到POD上,所以你最好自己写个自定义的复制ctor”时能派上用场。

表1-2 常用缩略词的意思

时间: 2024-09-23 11:43:04

《C++覆辙录》——1.9:使用糟糕的语言的相关文章

《C++覆辙录》——导读

前言 C++覆辙录 本书之渊薮乃是近20年的小小挫折.大错特错.不眠之夜和在键盘的敲击中不觉而过的无数周末.里面收集了普遍的.严重的或有意思的C++常见错误,共计九十有九.其中的大多数,(实在惭愧地说)都是我个人曾经犯过的. 术语"gotcha"1有其云谲波诡的形成历史和汗牛充栋的不同定义.但在本书中,我们将它定义为C++范畴里既普遍存在又能加以防范的编码和设计问题.这些常见错误涵盖了从无关大局的语法困扰,到基础层面上的设计瑕疵,再到源自内心的离经叛道等诸方面. 大约10年前,我开始在

《C++覆辙录》——常见错误1:过分积极的注释

第1章 基础问题 C++覆辙录 说一个问题是基础的,并不就是说它不是严重的或不是普遍存在的.事实上,本章所讨论的基础问题的共同特点比起在以后章节讨论的技术复杂度而言,可能更侧重于使人警醒.这里讨论的问题,由于它们的基础性,在某种程度上可以说它们普遍存在于几乎所有的C++代码中. 常见错误1:过分积极的注释 很多注释都是画蛇添足,它们只会让源代码更难读,更难维护,并经常把维护工程师引入歧途.考虑下面的简单语句: a = b; // 将b赋值给a 这个注释难道比代码本身更能说明这个语句的意义吗?因而

《C++覆辙录》——第2章 语法问题2.1:数组定义和值初始化的语法形式混淆

第2章 语法问题 C++覆辙录C++语言的语法和词法结构博大精深.此复杂性的一部分是从C语言那里继承而来的,另一部分则是为支撑某些特定的语言特性所要求的. 本章中我们将考察一组语法相关的头疼问题.其中有些属于常见的手误,但是错误的代码仍然能够通过编译,只不过会以出人意料的方式运行罢了.另外一些则是由于一段代码的语法结构及它们的运行期行为不再互为表里.其余的部分,我们主要研究语法层面的灵活余地带来的问题:明明是一字不差的代码,不同的软件工程师能从中得出大相径庭的结论来. 2.1:数组定义和值初始化

《C++覆辙录》——1.5:对引用的认识误区

1.55:对引用的认识误区 对于引用的使用,主要存在两个常见的问题.首先,它们经常和指针搞混.其次,它们未被充分利用.好多在C++工程里使用的指针实际上只是C阵营那些老顽固的杰作,该是引用翻身的时候了. 引用并非指针.引用只是其初始化物的别名.记好了,能对引用做的唯一操作就是初始化它.一旦初始化结束,引用就是其初始化物的另一种写法罢了(凡事皆有例外,请看常见错误44).引用是没有地址的,甚至它们有可能不占任何存储: int a = 12; int &ra = a; int *ip = &r

《C++覆辙录》——1.3:全局变量

1.3:全局变量 很难找到任何理由去硬生生地声明什么全局变量.全局变量阻碍了代码重用,而且使代码变得更难维护.它们阻碍重用是因为任何使用了全局变量的代码就立刻与之耦合,这使得全局变量一改它们也非得跟着改,从而使任何重用都不可能了.它们使代码变得更难维护的原因是很难甄别出哪些代码用了某个特定的全局变量,因为任何代码都有访问它们的权限. 全局变量增加了模块间的耦合,因为它们往往作为幼稚的模块间消息传递机制的设施存在.就算它们能担此重任,从实践角度来说8,要从大型软件的源代码中去掉任何全局变量都几乎不

《C++覆辙录》——1.7:无视基础语言的精妙之处

1.7:无视基础语言的精妙之处 大多数C++软件工程师都自信满满地认为自己对所谓C++的"基础语言",也就是C++继承自C语言的那部分了如指掌.实际情况是,即使经验丰富的C++软件工程师,有时也会对最基础的C/C++语句和运算符的某些妙用一无所知. 逻辑运算符不能算难懂,对吗?但刚入行的C++软件工程师却总是不能让它们物尽其用.你看到下面的代码时是不是会怒从胆边生? bool r = false; if( a < b ) r = true;``` 正解如下: bool r = a

《C++覆辙录》——1.4:未能区分函数重载和形参默认值

1.4:未能区分函数重载和形参默认值 函数重载和形参默认值之间其实并无干系.不过,这两个独立的语言特征有时会被混淆,因为它们会模塑出语法上非常相像的函数用法接口.当然,看似一样的接口其背后的抽象意义却大相径庭: class C1 { public: void f1( int arg = 0 ); // ... };``` // ... C1 a; a.f1(0);a.f1();`型别C1的设计者决定给予函数f1()一个形参的默认值.这样一来,C1的使用者就有了两个选择:要么显式地给函数f1()一

《C++覆辙录》——2.9:自反初始化

2.9:自反初始化 在以下的代码里,var的值变成了多少? int var = 12; { double var = var; // ... }``` 未有定义.C++语言中,某个名字在它的初始化对象被解析到之前就进入了其辖域的话,在初始化对象引用到这个名字时,它引用到的不是别的,正是这个刚刚被声明的对象.没有几个软件工程师会写出像上面这么莫名其妙的声明代码,但也许复制.粘贴的手法会让你陷入困境: int copy = 12; // 某深藏不露的变量// ...int y = (3x+2copy

《C++覆辙录》——1.2:幻数

2:幻数 幻数,用在这里时其含义是上下文里出现的裸字面常量(raw numeric literal),本来它们应该是具名常量(named constant)才对: class Portfolio { // ... Contract *contracts_[10]; char id_[10]; };``` 幻数带来的主要问题是它们没有(抽象)语义,它们只是个量罢了.一个"10"就是一个"10",你看不出它的意思是"合同的数量"或是"标识符