来源:远方网络 | 2006-1-6 | (有1931人读过)
STL 是标准模板库(Standard Template Library),作为 C++ 的标准库的一部分,致力于容器(containers)(例如,vector,list,set,map,等等),迭代器(iterators)(例如,vector<int>::iterator,set<string>::iterator,等等),算法(algorithms)(例如,for_each,find,sort,等等),以及相关机能。相关机能中的很多都通过函数对象(function objects)——行为表现类似于函数的对象——提供。这样的对象来自于重载了 operator() ——函数调用运算符——的类,如果你不熟悉 STL,在读本书的时候,你应该有一本像样的参考手册备查,因为对于我来说 STL 太有用了,以至于不能不利用它。一但你用了一点点,你也会有同样的感觉。
从 Java 或 C# 那样的语言来到 C++ 的程序员可能会对未定义行为(undefined behavior)的概念感到吃惊。因为各种各样的原因,C++ 中的一些结构成分(constructs)的行为没有确切的定义:你不能可靠地预知运行时会发生什么。这里是两个带有未定义行为的代码的例子:
int *p = 0; // p is a null pointer
std::cout << *p; // dereferencing a null pointer // yields undefined behavior char name[] = "Darla"; // name is an array of size 6 (don’t // forget the trailing null!)
char c = name[10]; // referring to an invalid array index // yields undefined behavior
为了强调未定义行为的结果是不可预言而且可能是令人讨厌的,有经验的 C++ 程序员常常说带有未定义行为的程序能(can)删除你的硬盘。这是真的:一个带有未定义行为的程序可以(could)删除你的硬盘。只不过可能性不太大。更可能的是那个程序的表现反复无常,有时会运行正常,有时会彻底完蛋,还有时会产生错误的结果。有实力的 C++ 程序员能以最佳状态避开未定义行为。本书中,我会指出许多你必须要注意它的地方。
另一个可能把从其它语言转到 C++ 的程序员搞糊涂的条目是接口(interface)。Java 和 .NET 的语言都将接口作为一种语言要素,但是在 C++ 中没有这种事。当我使用条目“接口(interface)”时,一般情况下我说的是一个函数的识别标志,是一个类的可访问元素(例如,一个类的 "public interface","protected interface",或 "private interface"),或者是对一个模板的类型参数来说必须合法的表达式。也就是说,我是作为一个相当普遍的设计概念来谈论接口(interface)的。
客户(client)是使用你写的代码(一般是接口(interfaces))的某人或某物。例如,一个函数的客户就是它的用户:调用这个函数(或持有它的地址)的代码的片段以及写出和维护这样的代码的人。类或者模板的客户是使用这个类或模板的软件的部件,以及写出和维护那些代码的程序员。在讨论客户的时候,我一般指向程序员,因为程序员会被困扰和误导,或者因为不好的接口而烦恼。但他们写的代码却不会。
你也许不习惯于为客户着想,但是我会用大量的时间试图说服你:你应该尽你所能使他们的生活更轻松。记住,你也是一个其他人开发的软件的客户。难道你不希望那些人为你把事情弄得轻松些吗?除此之外,在某种程度上,你几乎肯定能发现你自己处在了你自己的客户的位置上(也就是说,使用你写的代码),而这个时候,你会为你在开发你的接口时在头脑中保持了对客户的关心而感到高兴。
我常常掩盖函数和函数模板之间以及类和类模板之间的区别。那是因为对其中一个确定的事对另一个常常也可以确定。如果不是这样,我会区别对待类,函数,以及由类和函数产生的模板。
在代码注释中提到构造函数和析构函数时,我有时使用缩写形式 ctor 和 dtor。
Naming Conventions 命名惯例
我试图为对象,类,函数,模板等选择意味深长的名字,但是在我的某些名字后面的含义可能不会立即显现出来。例如,我特别喜欢的两个参数名字是 lhs 和 rhs。它们分别代表 "left-hand side" 和 "right-hand side"。我经常用它们作为实现二元运算符的函数(例如,operator== 和 operator*)的参数名。例如,如果 a 和 b 是代表有理数的对象,而且如果 Rational 对象能通过一个非成员的 operator* 函数相乘(Item 24 中解释的很可能就是这种情况),表达式
a * b
与函数调用
operator*(a,b)
就是等价的。
我也这样声明 operator*过:
const Rational operator*(const Rational& lhs, const Rational& rhs);
你可以看到,左手操作数(left-hand operand)a 在函数内部以 lhs 的面目出现,而右手操作数(right-hand operand)b 以 rhs 的面目出现。
对于成员函数左手参数(left-hand argument)表现为 this 指针,所以有时候我单独使用参数名 rhs。你可能已经在第 5 页中某些 Widget 成员函数的声明(本文介绍拷贝构造函数的那一段中的例子——译者注)中注意到了这一点。这一点提醒了我。我经常在示例中使用 Widget 类。"Widget" 并不意味着什么东西。它仅仅是在我需要一个示例类的名字的时候不时地使用一下的名字。它和 GUI 工具包中的 widgets 没有任何关系。
我经常遵循这个规则为指针命名:一个指向类型 T 的对象的指针被称为 pt,"pointer to T"。以下是例子:
Widget *pw; // pw = ptr to Widget
class Airplane; Airplane *pa; // pa = ptr to Airplane class GameCharacter; GameCharacter *pgc; // pgc = ptr to GameCharacter
我对引用使用类似的惯例:rw 可以认为是一个引向 Widget 的引用,而 ra 是一个引向 Airplane 的引用。
在我讨论成员函数的时候我偶尔会使用名字 mf。
Threading Considerations 对线程的考虑
作为一种语言,C++ 没有线程的概念——实际上,是没有任何一种并发的概念。对于 C++ 标准库也是同样如此。就 C++ 涉及的范围而言,多线程编程并不存在。
而且至今它们依然如此。我致力于让此书基于标准的,可移植的 C++,但我也不能对线程安全(thread safety)已成为很多程序员所面临的一个问题的事实视而不见。我对付这个标准 C++ 和现实之间的裂痕的方法就是指出某处的 C++ 结构成分(constructs)以我的分析很可能在多线程环境中引起问题的地方。这样不但不会使本书成为一本用 C++ 进行多线程编程的书。反而,它更会使本书在相当程度上成为这样一本 C++ 编程的书:将自己在很大程度上限制于单线程考虑,承认多线程的存在,并试图指出有线程意识的程序员需要特别当心评估我提供的建议的地方。
如果你不熟悉多线程编程或者不必为此担心,你可以忽略我关于线程的讨论。如果你正在编写一个多线程的应用或库,无论如何,请记住我的评注和并将它作为你使用 C++ 时需要致力去解决的问题的起点。
TR1 and Boost TR1 和 Boost
你会发现提及 TR1 和 Boost 的地方遍及整个系列。它们每一个都专门在某些细节上进行描述,但是,不幸的是,这些都在整个系列的最后。(他们在那里是因为那样更好一些,我确实试过很多其它的地方。)如果你愿意,但是如果你更喜欢从本书的起始处而不是结尾处开始,以下摘要会对你有所帮助:
·TR1 ("Technical Report 1") 是被加入 C++ 标准库的新机能的规格说明书。这些机能以新的类和函数模板的形式提供了诸如哈希表(hash tables),引用计数智能指针(reference-counting smart pointers),正则表达式(regular expressions),等等。所有的 TR1 组件都位于嵌套在 namespace std 内部的 namespace tr1 内。
·Boost 是一个组织和一个网站 (http://boost.org) 提供的可移植的,经过同行评审的,开源的 C++ 库。大多数 TR1 机能都基于 Boost 的工作,而且直到编译器厂商在他们的 C++ 库发行版中包含 TR1 之前,Boost 网站很可能会保持开发者寻找 TR1 实现的第一站的地位。Boost 提供的东西比用于 TR1 的更多,无论如何,在很多情况下,它还是值得去了解一下的。
|