基础议题
Basics
基础议题。是的,pointers(指针)、references(引用)、casts(类型转换)、arrays(数组)、constructors(构造函数)--再没有比这些更基础的议题了。几乎最简单的C++(www.cppentry.com) 程序也会用到其中大部分特性,而许多程序会用到上述所有特性。
尽管你可能已经十分熟悉语言的这一部分,但有时候它们还是会令你吃惊。特别是对那些从C 转战到C++(www.cppentry.com) 的程序员,因为references,dynamic casts,default constructors 及其他non-C性质背后的观念,往往带有一股黝黯阴郁的色彩。
这一章描述 pointers 和 references 的差异,并告诉你它们的适当使用时机。本章介绍新的C++(www.cppentry.com) 转型(casts)语法,并解释为什么新式转型法比旧式的C 转型法优越。本章也检验了C的数组概念及 C++(www.cppentry.com) 的多态(polymorphism)概念,并说明为什么将这两者混用是不智之举。最后,本章讨论 default constructors(默认构造函数)的正方和反方意见,并提出一些建议做法,让你回避语言的束缚(因为在你不需要default constructors 的情况下,C++(www.cppentry.com) 也会给你一个)。
只要留心下面各条款的各项忠告,你将向着一个很好的目标迈进:你所编写的软件可以清楚而正确地表现出你的设计意图。
条款1:仔细区别pointers和references
Pointers 和 references 看起来很不一样(pointers 使用"*"和"->"操作符,references则使用"."),但它们似乎做类似的事情。不论 pointers 或是 references 都使你得间接参考其他对象。那么,何时使用哪一个?你心中可有一把尺?
首先你必须认知一点,没有所谓的 null reference。一个 reference 必须总代表某个对象。所以如果你有一个变量,其目的是用来指向(代表)另一个对象,但是也有可能它不指向(代表)任何对象,那么你应该使用pointer,因为你可以将pointer 设为 null。换个角度看,如果这个变量总是必须代表一个对象,也就是说如果你的设计并不允许这个变量为 null,那么你应该使用 reference。
"但是等等"你说,"下面这样的东西,底层意义是什么呢?"
- char *pc = 0; // 将 pointer 设定为 null。
- char& rc = *pc; // 让 reference 代表 null pointer 的解引值。
哦,这是有害的行为,其结果不可预期(C++(www.cppentry.com) 对此没有定义),编译器可以产生任何可能的输出,而写出这种代码的人,应该与大众隔离,直到他们允诺不再有类似行为。如果你在你的软件中还需担心这类事情,我建议你还是完全不要使用 references 的好,要不就是另请一个比较高明的程序员来负责这类事情。从现在起,我们将永远不再考虑"reference 成为 null"的可能性。
由于 reference 一定得代表某个对象,C++(www.cppentry.com) 因此要求 references 必须有初值:
- string& rs; // 错误!references 必须被初始化。
- string s("xyzzy");
- string& rs = s; // 没问题,rs 指向 s。
但是 pointers 就没有这样的限制:
- string *ps; // 未初始化的指针,有效,但风险高。
"没有所谓的 null reference"这个事实意味使用 references 可能会比使用 pointers 更富效率。这是因为使用 reference 之前不需要测试其有效性:
- void printDouble(const double& rd)
- {
- cout << rd; // 不需要测试 rd,它一定代表某个 double。
- }
如果使用 pointers,通常就得测试它是否为 null:
- void printDouble(const double *pd)
- {
- if (pd) { // 检查是否为 null pointer。
- cout << *pd;
- }
- }
Pointers 和 references 之间的另一个重要差异就是,pointers 可以被重新赋值,指向另一个对象,reference 却总是指向(代表)它最初获得的那个对象:
- string s1("Nancy");
- string s2("Clancy");
-
- string& rs = s1; // rs 代表 s1。
- string *ps = &s1; // ps 指向 s1。
- rs = s2; // rs 仍然代表 s1,
- // 但是 s1 的值现在变成了"Clancy"。
- ps = &s2; // ps 现在指向 s2,
- // s1 没有变化。
一般而言,当你需要考虑"不指向任何对象"的可能性时,或是考虑"在不同时间指向不同对象"的能力时,你就应该采用 pointer。前一种情况你可以将 pointer 设为 null,后一种情况你可以改变 pointer 所指对象。而当你确定"总是会代表某个对象",而且"一旦代表了该对象就不能够再改变",那么你应该选用 reference。
还有其他情况也需要使用 reference,例如当你实现某些操作符的时候。最常见的例子就是 operator[]。这个操作符很特别地必须返回某种"能够被当做 assignment 赋值对象"的东西:
- vector<int> v(10); // 产生一个 int vector,大小为 10。
- // vector 是 C++(www.cppentry.com) 标准程序库(见条款 35)
- // 提供的一个 template。
- v[5] = 10; // assignment 的赋
值对象是 operator[] 的返回值。
如果 operator[] 返回 pointer,上述最后一个语句就必须写成这样子:
- *v[5] = 10;
但这使v 看起来好像是个以指针形成的 vector,事实上它不是。为了这个因素,你应该总是令 operator[] 返回一个 reference。条款30 有一个例外,十分有趣。
因此,让我做下结论:当你知道你需要指向某个东西,而且绝不会改变指向其他东西,或是当你实现一个操作符而其语法需求无法由 pointers 达成,你就应该选择 references。任何其他时候,请采用 pointers。