引言
从java或者c#过度过来的编程者可能看到“undefined behavior”这个错误提醒时会感到很惊讶。由于种种原因,c++的有些动作是没有规定好的,也就是说你不能预期在运行期间会产生什么样的结果。下面是两个例子:
-
int *p=0; ///注意这里把地址设为0,意为p是一个null的指针
-
cout<<*p; //////解引用一个null指针,会产生不可预期的结果
-
char name[]="Darla"; ////// 你猜这个数组的大小是多少?呵呵,应该是6,因为末尾还有一个'\0'
-
char c =name[10]; ///数组地址越界,可能产生不可预期的结果
当然了,作者这里的意思是这些代码在编译的时候可以通过,但是在运行的时候不同的机器会产生不同的结果,我在ubuntu下实验时,编译是没有问题的,不会提醒地址越界什么的,但是运行的时候会提示说"Segmentation fault (core dumped)",也就是对非法地址进行了引用。
为了强调未知行为可能会产生不可预知的痛苦的结果,一些有经验的c++程序员经常说不可预知行为可能会让你的硬盘内容都被清除,这是真的:一个未知行为的代码可能会删除你的硬盘的东西。但是这种可能微乎其微。很可能是这种代码会产生稀奇古怪的结果,有时正常运行,有时直接宕掉,有时会产生不正确的结果。有效的C++程序员就是要尽最大可能消除不可预知行为。在这本书中我指出了不少不可预知行为。
另外一个可能让c++程序员困惑的概念可能是“接口”(interface)。java和.NET语言把接口当做语言的一个元素,但是在C++中没有这种东西。当我使用接口这个词时,我一般是指一个函数的标签,一个类的入口或者一个模板的类型变量的表达式(详见条目41)。也即是说我谈论的接口时通用的设计概念中的接口,而不是像java语言里的一个语言的元素那样。作者的意思是在c++中没有特意的接口的概念,其实java的接口也是为了实现“多重继承”,在C++里不是用接口来实现多重继承。
所谓的用户是指使用你的代码的人或者其它什么东西。例如一个函数的用户:调用这个函数的那段代码,以及编写和维护这段代码的人。一个类或者模板的用户是指一个软件中调用这个类或者模板的那段代码,以及写这段代码的人。当我讨论用户时,我一般是指代码编写者,因为编程者可能会因为设计差的接口而感觉困惑,被误导或者是被激怒。
也许你不习惯用用户这个概念来思考问题,但是我会花一些时间来说服你接受这个观点。如果你是一个软件的用户,难道你不想让软件设计者设计出来的软件简单好用吗?除此之外,你会发现你既是代码设计者又是代码的用户,在这时,你就会庆幸你在设计接口时考虑了用户这个概念。也就是说你要时刻考虑用户体验。
在这本书中,我经验故意忽略了函数,函数模板,类以及类模板之间的区别。这时因为如果适用于函数、函数模板的概念也同样适用于类、类模板。当有些地方不适用时,我会特别之处的。
当介绍构造函数以及析构函数时,我通常使用ctor和dtor。
命名习惯
我努力为对象、类、函数以及模板选择有意义的名字,但是有些名字可能不是那么直观。我最喜欢的两个参数名是 lhs和rhs。他们分别代表的意思是'left-hand-side'和'right-hand-side'。在二元操作符中,我通常使用这两个参数。例如,如果a和b是两个有理数,并假设有理数可以通过非成员函数operator*来进行相乘,那么表达式:
等价于:
在条目24中,我将operator *声明为:
-
const Rational operator *(const Rational &lhs,const Rational &rhs)
如你所见,左边的操作数a,在函数中表现为lhs,右边的操作数b,在函数中变现为rhs。
对于成员函数,左边的参数是用this这个指针表示的,所以有时我只用rhs这一个参数名。
也许你注意到了我会使用叫做widget的成员函数名,我也经常使用widget来定义类。“widget”什么也不代表,我只是在需要一个class的时候把它当做一个例子来说明。它和GUI工具箱里的widget没有任何关系。翻者音:widget是小工具的意思,作者这是就是想说,我如果想用一个演示实例来说明某个原理,我就用widget这个符号。
我在命名指针时遵循的规则是:如果一个指针指向一个类型为T的对象,我们就把这个指针叫做'pt',意为“pointer to T”。
比如:
-
Widget *pw; //// pw=pointer to widget
-
class Airplane;
-
Airplane *pa;; /// pa=pointer to Airplane
-
class GamaCharacter;
-
GameCharacter *pgc; ///// pgc=pointer to GameCharacter
我在表述引用时也是采用相同的规则:rw可能只一个类型为Widget的对象的引用,ra可能是一个类型为Airplane的对象的引用。
我觉得作者这样做可能多此一举,但是老外都喜欢这样,一是显得比较正式,二是大牛们总是要显示自己的偏好才行啊!
基于线程的考虑
作为一门语言,C++没有线程的概念-----没有类似并发等类似的东西。标准库中也没有这个概念。说及C++,多线程是不存在的。
然而现实中是有多线程的(老外的习惯,先左再右)。我这本书的重点是放在标准的可移植的C++上,但是我不能忽略的一个事实就是许多程序员都面临线程安全问题。我采取的方式就是指出标准C++和现实有冲突或者有隔阂的点。这不是一本关于C++多线程编程的书。我们离多线程编程还差的远呢!但是,这本书将自己局限于单线程编程的同时,也承认多线程编程的现实需求,在多线程程序员需要特别注意的地方,我们也另外指出来了。
如果你对多线程编程不熟,或者不想关心它,你可以忽略我对于多线程的一些提示。但是如果你是一个线程应用或者是线程库程序员,你应该注意我的一些评论只不过是你需要关心的点的开始,你还需关心很多其它的东西。
翻者音:接触计算机快十年了,还是感觉自己是一只菜鸟,所懂的只是皮毛。对于一门语言也只是知道一些简单的规则,对于一些技术也只是可以知道怎么用。至于为什么要这么设计规则,为什么要这样设计接口提供给用户,还完全没有考虑过,我相信我不是个例,大多数的中国的学生都是这样的。当你在简历中大言不惭的说自己精通C,精通C++,精通JAVA后者什么的时候,你考虑过面试人员会问你什么问题吗?你以为他会傻傻的问你一些大路边上的问题?我确定他会问到你不能作答为止,因为只有这个时候他才知道你什么水平。
我目前为止认识到学习计算机的一个好处就是让我们认识到:任何东西都要符合符合逻辑才行!非计算机专业的人觉得计算机很厉害,什么都能做,那么计算机专业的你,就要说,错了,它什么都不会做,只会处理0和1.
也许可能其它学科也是这样,外行人觉得很高深,内行人觉得很简单,但是就是在这种高深与简单中创造了这变换莫测的大千世界!
单就一门计算机语言来说,其实你在学习的时候只要掌握一点就是:你的所有指令都要符合计算机的逻辑,都不能让计算机为难,所有动作都必须简单明了。如果出现了逻辑上的错误,要么计算机抱怨你,要么你的上司抱怨你。
就C++来说也是一样的,任何的代码一旦让计算机觉得不舒服,觉得除了0和1之外我好像还要处理其它的东西,OK,这个时候作为程序猿的你就应该停下来debug了。那么我们在学习那么语言规则的时候也是一样的,你会问为什么这样设计?答案很简单,就是为了让计算机这个铁盒子知道你要让它做什么。如果一些规则设计的模棱两可,那就不能称其为计算机语言规则。
计算机语言,流行的有很多种,我们都应该掌握吗?我觉得不然。我最近的一个思想是:计算机世界里,所有的东西都是抽象的,只是抽象的层次不同而已。计算机语言也是的。从0,1到汇编,到C,到python,到...,抽象的层次一步比一步高,我们应该研究哪个层次呢?如果你想研究0,1这个层次,可以,你就买一台70年代的电脑,带那种卡带的,你自己打卡带,输入给计算机,享受那种看着0,1变化的过程;如果你想研究汇编,可以,你把一段C或者C++代码改成汇编,然后运行,看到结果和C的一样,很好,很欢喜;如果你喜欢研究python,可以,你可以对那么用C写代码的程序猿说,看,我的代码多简单,只要几行就可以做那么多事
我说的这些只是个人观点。而且我认为计算机编程语言会越来越向着自然语言发展,因为一门计算机语言,核心的东西就是编译器吗,如果你设计一门语言,可以用汉字编码,例如:给我一个名字为苹果的整型变量,再给我一个名字为梨子的整型变量,然后将这两个变量相加,放在一个名字为橘子的变量中。对于这句话,你的编译器解释出来的内容如果和C的编译器解释出来的内容相同,OK啊,这就是一门语言。所以我说“一门计算机语言,就是一个编译器”,不知这话对不对,相信随着我对计算机的理解的深入这个观点还会改变。
难怪乔布斯说:我建议每个人都拿出一年的时间来学习一门编程语言。编程教会我们的是让我们要知道这个世界有规则,只有按照规则做事才行。所以我相信,IT男犯罪率应该很低,这除了跟教育、学历有关外,还应该跟他学过编程语言有关,哈哈!
阅读(711) | 评论(0) | 转发(0) |