1.1 C++简介
C++现在已经广泛应用于个人电脑、UNIX工作站和大型计算机。由于C++是建立在C语言基础之上的(在C++出现之前,许多环境都使用C语言),这很大程度上帮助了C++的普及。归纳起来,C++有如下优点。
· C++适用的应用程序范围极广。C++可以用于几乎所有的应用程序,从字处理应用程序到科学应用程序,从操作系统组件到计算机游戏等。
· C++可以用于硬件级别的编程,例如实现设备驱动程序。
· C++从C语言中继承了过程化编程的高效性,并集成了面向对象编程方式的功能。
· C++在其标准库中提供了大量的功能。
· 有许多商业C++库支持数量众多的操作系统环境和专门的应用程序。
由于几乎所有的计算机都可以使用C++编程,所以C++语言现在十分普及,同时用户把用C++编写的程序从一台机器迁移到另一台机器上不需要费多大力气。下面就介绍一下C++的ANSI/ISO标准。
1.1.1 ANSI/ISO标准
C++的国际标准由ISO/IEC 14882文档定义,该文档由美国国家标准协会ANSI发表。用户可以获得该标准的副本,但要记住,该标准主要由编译器编写人员使用,而不是由学习该语言的人使用的。用户可以从http://webstore.ansi.org/ansidocstore/default.asp上用合理的费用下载该标准的副本。
标准化是把所编写的程序从一种类型的计算机上迁移到另一种类型的计算机上的基础。标准的建立使语言在各种机器上的实现保持同步。在所有相容的编程系统上都可用的一组标准功能意味着,用户总是能确定下一步会获得什么结果。C++的ANSI标准不仅定义了语言,还定义了标准库。使用ANSI标准后,C++应用程序可以轻松地在不同的机器之间迁移,解决了在多个环境中运行的应用程序的维护问题。
C++的ANSI标准还有一个优点:它对用C++编程所需要学习的部分进行了标准化。这个标准将使后续的程序具有一致性,因为它只为C++编译器和库提供了一个定义参考。在编写编译器时,该标准的存在也使编写人员不再需要许可。读者在购买遵循ANSI标准的C++编译器时,就能知道将得到什么样的语言和标准库功能。
1.1.2 名称/标识符
C++程序中的许多元素都有用来表示它们的名称,也称为标识符。在C++程序中,可以命名5种元素,具体如下。
(1)函数。函数是自包含的、可执行代码的命名块。第8章将详细讨论如何定义函数。
(2)变量。变量是内存中的指定区域,用于存储数据项。第2章将论述变量。
(3)类型。类型是可以存储的数据种类,例如类型int用于存储整数。第2章和后续的章节将介绍类型,尤其是第11章。
(4)标签。标签提供了表示特定语句的方式。它们很少使用,第4章将详细介绍。
(5)命名空间。命名空间是用一个集合名称标识程序中一组命名项的方式。这听起来可能让人迷惑,但不必担心,第10章将详细论述。
在C++中,名称可以包含大小写拉丁字母a~z和A~Z、下画线(_)和数字0~9。C++的ANSI标准还允许在名称中包含通用字符集(Universal Character Set)中的字符。
ANSI标准还允许名称有任意长度,但有的编译器对此有某种限制,这个限制常常比较宽(几千个字符),不是一个严格的限制。
空白在C++中用于表示空格、垂直或水平制表符、换行符和换页符。不能在名称的中间加上空白字符,否则,编译器就不会把该名称看做是一个名称,而是会看做两个或多个名称,从而导致处理不正确。另一个限制是名称不能以数字开头。
下面是一些合法的名称例子:
value2 Mephistopheles BettyMay Earth_weight PI
下面这些名称则不合法:
8Ball Mary-Ann Betty+May Earth-weight 2PI
提示 包含两个下画线的名称,或者以下画线开头后跟一个大写字母的名称,是C++标准库的保留名称,在程序中不应使用这类名称。编译器不会检查这类名称,用户只能在程序出错时发现有一个冲突的名称。
1.1.3 使用扩展字符集的名称
C++标准允许在名称中包含UCS字符。可以把这些字符写做\Udddddddd或\udddd的格式,其中d是UCS码中某字符的16进制数字。
但没有人希望在名称中包含这样的字符,在名称中嵌入\U后跟一组16进制数字不会提高代码的可读性。允许在名称中使用UCS字符,是为了让编译器编写人员可以包容用非英语的其他语言编写的字符,例如希腊语、韩语或俄语。
C++标准允许实现编译器,以使用任何字符来指定名称。利用这一点的所有编译器在开始编译代码之前,都必须把非基本集合的字符转换为如前面所示的UCS字符的标准表示。例如,在源代码中,有人编写了名称KHHRa,它对俄国程序员来说是有意义的。编译器在编译代码之前,先在内部把这个名称转换为UCS字符的一个标准化表示,例如/u041A/u043D/u0438/u0433/u0430。实际上,无论在源代码中使用什么字符集指定名称,最终都会得到基本集合中的字符,再加上UCS字符\Udddddddd或\udddd。
在名称中,必须总是显式使用基本字符集中的字符a~z、A~Z、0~9和下画线。在名称中为这些字符使用UCS码是非法的。其原因是标准没有指定用于基本字符集的编码,所以这个任务就留给了编译器。因此,如果要根据UCS码指定基本字符,该字符可能不同于显式指定字符时编译器为该字符使用的编码,从而产生混乱的结果。
注意 并没有要求编译器支持在指定名称时显式使用字符,如果编译器不支持这些字符,在处理之前,这些字符就必须映射为UCS格式。遵循该标准的编译器必须在任何情况下都支持名称使用基本字符集中的字符,并允许以本节开头介绍的不太友好的方式使用UCS字符。
1.1.4 命名空间
在前一节讨论的标识符规则中,可以为程序中的元素选择使用任何名称。显然,可以为标准库中已经用于其他目的的元素选择一个名称。如果两个或多个程序员为同一个大型工程的不同部分工作,就会有潜在的名称冲突。当两个或多个不同的元素使用相同的名称时会导致冲突,命名空间用于解决这个问题。
命名空间的名称有点像姓氏。家庭中的每个成员都有自己的姓名,在大多数家庭中,每个家庭成员都有一个唯一的名字。例如在Smith家中,有Jack、Jill、Jean和Jonah。在家庭成员之间,可以用名字来指代每个人。但是,其他家庭的成员可能与Smith家的成员有相同的名字。例如,在Jones家中,其成员的名字是John、Jean、Jeremiah和Jonah。Jeremiah Jones在称呼Jean时,显然是指Jean Jones。如果他想指代Smith家中的Jean,就要使用全名Jean Smith。如果不是这两个家庭的成员,就只能使用每个人的全名来指代他本人,例如Jack Smith或Jonah Jones。
这就是命名空间的作用。命名空间的名称类似于姓氏。在命名空间内部,可以使用其成员的名字。在命名空间的外部,就只能把某个实体的名字和命名空间的名称组合起来,表示该命名空间中的实体。命名空间的目的是提供一种机制,使大程序的各个部分中因出现重名而导致冲突的可能性降到最低。一般情况下,一个程序中包含几个不同的命名空间。
C++标准库中的实体都是在命名空间std中定义的,所以标准库中的所有实体名都用std来限定。cout的全名就是std::cout,其中的两个冒号有一个非常好听的名称,即范围解析运算符,该运算符把命名空间的名称std和流的名称cout分隔开来。
1.1.5 代码注释
程序代码的注释是非常重要的。在编写代码时,这些代码的含义是很清晰的,但过一个月后再看这些代码,它们的含义就很模糊了。因此可以用注释解释代码,在C++中注释有两种形式:单行注释和多行注释(注释可以放在几行上)。
单行注释以双斜杠开头(//)。例如:
//这个函数的功能是计算成本
编译器会忽略双斜杠后面的所有内容,但这并不表示注释要占满一整行。可以使用这种类型的注释来解释任何一条语句:
length = shrink(length, temperature);
也可以在该代码行的开头加上双斜杠,临时删除该行代码:
//length = shrink(length, temperature);
这会把该语句转换为一个注释,例如在测试程序时就可以这么做。代码行中从第一个双斜杠“//”开始到该行末尾的所有内容都会被忽略,包括其中的所有“//”。
多行注释有时用于编写较烦琐的、一般描述性材料,例如解释函数中使用的算法。这种注释以“/*”开头,以“*/”结尾。在“/*”和“*/”之间的所有内容都将被忽略。这可以修饰多行注释,以突出显示它们,例如:
/****************************************** *这个函数的功能是计算成本 * * 运用现在的仿真技术* ******************************************/
还可以使用这种注释临时禁用一个代码块。方法是在代码块的开头加上“/*”,在末尾加上“*/”。但是,必须注意/*…*/注释不能嵌套,否则会使编译器发出错误消息。因为内层嵌套注释的结束“*/”会与外层注释的开始“/*”进行匹配:
// 你不必嵌套多余的注释 /* 一个外部注释 /* 这是个内部注释,因为外面有注释 并且,结尾也是在内部,外面没有结尾*/ 如果外面没有注释,计算机将编译*/
外层注释的最后一部分并没有放在注释块中,编译器会试图编译它,显然这会导致错误。所以,“//”形式的注释在C++程序中应用最广泛。
提示 多行注释有时也称为C样式的注释,这是因为/*…*/语法只能用在C语言中,以创建注释。
1.1.6 标准库
标准库包含了大量的函数和其他支持实体,增加和扩展了C++的基本语言功能。标准库的内容是C++的一部分,在语言的语法和语义方面跟C++相同。C++的标准定义了这两者,所以每个符合该标准的编译器都提供了完整的标准库。
标准库的范围是很特殊的。使用标准库将获得非常多的功能,包括基本元素如基本语言支持、输入输出函数、异常处理(异常是在程序执行过程中发生的偶然事件,常常是某种错误)、实用函数、数学例程和各种预先编写好并测试通过的功能。在程序执行过程中可借助这些功能来存储和管理数据。
要高效地使用C++,应非常熟悉标准库的内容。本书在介绍C++语言时,将讨论标准库的许多功能,但本书介绍的内容肯定是不完整的。要完整地介绍标准库的功能和用法,需要用与本书同样篇幅的另一本书来讨论。
使用标准库所需要的定义和声明位于前面介绍过的标准头文件中。在有些情况下,标准头文件默认包含在程序文件中,但在大多数情况下,必须添加一个#include指令,把要使用的库功能所在的头文件包含进来。附录C中列出了一个完整的头文件列表,并简要说明了每个头文件支持的功能。
C++标准库中的几乎所有内容都是在命名空间std中定义的。也就是说,库中使用的所有名称都应加上前缀std。如本章前面所述,在引用标准库中的内容时,需要在名称前面加上前缀std,如下面的语句所示:
std::cout << "The best place to start is at the beginning";
另外,也可以在源文件的开头加上一个using指令:
using std::cout;
这样就可以使用名称cout,而不必给它添加std前缀了,因此可以使用下面的语句:
cout<<"The best place to start is at the beginning";
还可以把std命名空间的名称引入程序文件:
using namespace std;
这样,在程序中包含的头文件所定义的所有标准库名称就可以省略前缀std了。但是,这也有一个缺点:自己所定义的名称与标准库头文件定义的名称可能发生冲突。
本书总是在需要的代码段中包含std命名空间前缀。在完整的程序中,一般要为在代码中重复使用的标准库名称添加using语句。只使用一两次的名称可以用命名空间的名称来限定。