第2章 C++程序的组成部分
C++程序由类、函数、变量及其他元素组成。本书的大部分内容将对这些组成部分进行深入解释,但为了解程序是如何组合在一起的,必须分析一个完整的工作程序。
在本章中,您将学习:
• C++程序的组成部分;
• 各部分如何协同工作;
• 函数及其用途;
• 基本输入输出操作。
2.1 Hello World程序的组成部分
在第 1章中,您编写的第一个C++程序只是将句子Hello World打印到屏幕上,虽然如此,却包含了C++程序最重要的基本构件。本节将以程序清单2.1为例,对所有程序都包含的重要部分进行分析。
程序清单2.1 HelloWorldAnalysis.cpp:分析C++程序
可将这个C++程序划分为两个部分:以#打头的预处理器编译指令以及以 int main()打头的程序主体。
第1、4、7和10行以//或/*打头,它们都是注释,将被编译器忽略。这些注释是供人类阅读的。注释将在下一节更详细地讨论。
顾名思义,预处理器是一个在编译前运行的工具。预处理器编译指令是向预处理器发出的命令,总是以符号#打头。在程序清单 2.1 中,第 2 行的#include<filename>让预处理器获取指定文件(这里是iostream)的内容,并将它们放在编译指令所处的位置。iostream是一个标准头文件,这里包含它是因为第8行使用了 std::cout将Hello World打印到屏幕上,而该头文件包含 std::cout的定义。换句话说,编译器之所以能够编译包含std::cout的第8行,是因为我们在第2行指示预处理器包含了std::cout的定义。
在专业人员编写的C++应用程序中,包含的文件并非都是标准头文件。编写复杂的应用程序时,通常将其代码放在多个文件中,这使得需要在某些文件中包含其他文件。因此,如果需要在FileA中使用FileB中定义的元素,就需要在前者中包含后者。为此,通常在FileA中使用如下include语句:
这里使用引号而不是尖括号来包含自己创建的头文件。尖括号(<>)通常用于包含标准头文件。
预处理器编译指令的后面是程序的主体——main()函数,执行 C++程序时总是从这里开始。声明main()时,总是在它前面加上int,这是一种标准化约定,表示main()函数的返回类型为int。
在很多C++应用程序中,都使用了类似于下面的main()函数变种:
这符合标准且可以接受,因为其返回类型为int。括号内的内容是提供给程序的参数。该程序可能允许用户执行时提供命令行参数,如:
其中/DoSomethingSpecific是操作系统传递给程序的参数,以便在main中进行处理。
下面来看第8行,它实际执行该程序的功能:
cout(控制台输出,读作see-out)是将Hello World打印到屏幕上的语句。cout是在标准名称空间中定义的一个流(因此这里使用了std::cout),这里使用流插入运算符<<将文本Hello World放到这个流中。std::endl用于换行,将其插入流中相当于插入回车。每次需要将新实体插入流中时,都使用了流插入运算符。
C++流的优点是,将类似的语义用于另一种类型的流时,将执行不同的操作,如插入到文件而不是控制台。因此,流的用法非常直观,使用过一种流(如将文本写入控制台的 cout)后,其他流(如帮助将文本文件写入磁盘的fstream)使用起来就非常容易。
流将在第27章更详细地讨论。
实际文本(包括引号)“Hello World”被称为字符串字面量。
在C++中,除非明确声明了不返回值,否则函数必须返回一个值。main()也是函数,且总是返回一个整数。这个值返回给操作系统,根据应用程序的性质,这可能很有用,因为大多数操作系统都提供了查询功能,让您能够获悉正常终止的应用程序的返回值。在很多情况下,一个应用程序被另一个应用程序启动,而父应用程序(启动者)想知道子应用程序(被启动者)是否成功地完成了其任务。程序员可使用main()的返回值向父应用程序传递成功或错误状态。
根据约定,程序员在程序运行成功时返回 0,并在出现错误时返回−1。然而,返回值为整数,程序员可利用整个整数范围,指出众多不同的成功或失败状态。
C++区分大小写,如果将int写成了Int、将void写成了Void或将std::cout写成了Std::Cout,程序将不能通过编译。
2.2 名称空间的概念
在这个程序中,使用的是std::cout而不是cout,原因在于cout位于标准(std)名称空间中。
那么什么是名称空间呢?
假设调用cout时没有使用名称空间限定符,且编译器知道cout存在于两个地方,编译器应调用哪个呢?当然,这会导致冲突,进而无法通过编译。这就是名称空间的用武之地。名称空间是给代码指定的名称,有助于降低命名冲突的风险。通过使用std::cout,可命令编译器调用名称空间std中独一无二的cout。
您使用std(读作standard)名称空间来调用获得ISO标准委员会批准,并在该名称空间中声明的函数、流和工具。
很多程序员发现,使用cout和std名称空间中的其他功能时,在代码中添加std限定符很繁琐。为避免添加该限定符,可使用声明 using namespace,如程序清单 2.2所示。
程序清单2.2 using namespace声明
分析:
请注意第8行。通过告诉编译器您要使用名称空间std,在第11行使用cout和endl时,就无需显式地指定名称空间了。
程序清单2.3是程序清单2.2的更严谨版本,它没有包含整个名称空间,而只包含要使用的元素。
程序清单2.3 关键字using的另一种用法
分析:
在程序清单2.3中,使用第7~8行替换了程序清单2.2的第8行。两者的差别在于,前者让您能够在不显式指定名称空间限定符std::的情况下使用名称空间std中的所有元素,而后者让您能够在不显式指定名称空间限定符std::的情况下使用std::cout和std::endl。
2.3 C++代码中的注释
在程序清单2.3中,第1、4、10和13行包含口语(这里为英语)文本,但不会影响程序编译,也不会影响程序的输出。这些行被称为注释。注释会被编译器忽略,程序员使用它们来对代码进行解释,因此使用人类能够明白的语言编写。
C++支持下面两种风格的注释。
• //指出当前行为注释,例如:
• /和/表示它们之间的文本为注释,即便这些文本跨越多行:
程序员为何要对自己编写的代码进行解释呢?这看起来好像有点奇怪,但程序越大,合作开发同一个模块的程序员越多,编写易于理解的代码就越重要。必须使用清晰的注释对代码的功能以及为何要这样做进行解释,这很重要。
2.4 C++函数
C++函数与 C 语言函数相同。函数让您能够将应用程序划分成多个功能单元,并按您选择的顺序调用。函数被调用时,通常将一个值返回给调用它的函数。最著名的函数无疑是 main(),它被编译器视为C++应用程序的起点,必须返回一个整数。
程序员通常需要编写自己的函数。程序清单 2.4 是一个简单的应用程序,它使用一个函数在屏幕上显示内容,而该函数使用各种参数调用了std::cout。
程序清单2.4 声明、定义和调用函数,该函数演示了std::cout的一些功能
输出:
分析:
这里需要注意的是第5、10和15~25行。第5行为函数声明,它告诉编译器您要创建一个函数,该函数名为DemoConsoleOutput(),返回类型为int。正是因为该声明,编译器才会编译第10行,并假定后面会有函数定义(即函数的实现),这里是第15~25行。
这个函数演示了 cout的各种功能。它不仅能够像前面显示Hello World那样显示文本,还能显示简单算术运算的结果。第21和22行都试图显示pi的值(22/7),但后者更精确,因为22.0/7让编译器将结果视为实数(在C++中为float),而不是整数。
注意到该函数被声明为返回一个整数(这里返回的是0)。由于它不做任何决策,因此没必要返回其他值。同样,main()也返回0。鉴于main()将其所有任务都交给了函数DemoConsoleOutput()去完成,更明智的做法是,在main()中返回该函数的返回值,如程序清单2.5所示。
程序清单2.5 使用函数的返回值
分析:
该应用程序的输出与前一个程序清单相同,但编写方式存在细微的差别。首先,由于在 main()前面(第5行)定义了该函数,因此无需声明该函数。较新的C++编译器将其视为函数声明和定义。另外,main()也更简短。第19行调用函数DemoConsoleOutput(),并将该函数的返回值作为应用程序的返回值。
在函数无需做任何决策,也无需返回成功/失败状态时,可将其返回类型声明为void:
这个函数不能返回值。对于返回类型为void的函数,不能通过执行它来做决策。
函数可以接受参数,可以递归,可以包含多条返回语句,可以重载,还可声明为内联的,在这种情况下,编译器将展开函数调用。这些概念都将在第7章更详细地介绍。
2.5 使用std::cin和std::cout执行基本输入输出操作
计算机让用户能够以各种方式与其运行的应用程序交互,还让应用程序能够以众多方式与用户交互。用户通过键盘和鼠标与应用程序交互。您可以在屏幕上以文本和复杂图形的方式显示信息,使用打印机将信息打印到纸上,还可将信息存储到文件系统中,供以后使用。本节讨论最简单的 C++输入输出方式:使用控制台读写信息。
要将简单的文本数据写入到控制台,可使用 std::cout(读作 standard see-out);要从控制台读取文本和数字,可使用 std::cin(读作 standard see-in)。事实上,在程序清单 2.1中,在屏幕上显示Hello World时,您就使用过cout:
在这条语句中,cout的后面依次为插入运算符<<(帮助将数据插入输出流)、要插入的字符串字面量Hello World、使用 std::endl(读作 standard end-line)表示的换行符。
cin的用法也很简单,但它用于输入,因此需要指定要将输入数据存储到其中的变量:
因此,cin后面依次为提取运算符>>(从输入流中提取数据)以及要将数据存储到其中的变量。如果需要将用户输入存储到两个变量中——每个变量包含用空格分隔的数据,可使用一条语句完成这项任务:
请注意,cin可用于从用户那里获取文本输入和数字输入,如程序清单2.6所示。
程序清单2.6 使用cin和cout显示用户的数字输入和文本输入
输出:
分析:
第8行声明了一个名为InputNumber的变量,用于存储类型为int的数据。第10行使用cout让用户输入一个数字,而第13行使用cin将输入的数字存储在该int变量中。接下来重复相同的操作,存储用户的名字,当然这不能存储在int变量中,而是存储在string变量中,如第17~18行所示。第2行包含了<string>,原因是后面在main()中使用了类型string。最后,第20行使用一条cout语句显示输入的名字和数字以及连接文本,输出为Siddhartha entered 2011。
这是一个非常简单的示例,演示了 C++中输入输出的基本原理。如果您不清楚变量的概念,不用担心,第3章将详细阐述。
2.6 总结
本章介绍了简单 C++程序的基本组成部分。您明白了 main()是什么,了解了名称空间,学习了控制台输入输出的基本知识。现在,您能够在自己编写的任何程序中使用这些知识了。
2.7 问与答
问:#include的作用是什么?
答:这是一个预处理器编译指令。预处理器在您调用编译器时运行。该指令使得预处理器将#include后面的<>中的文件读入程序,其效果如同将这个文件输入到源代码中的这个位置。
问://注释和/*注释之间有何不同?
答://注释到行尾结束;/注释到/结束。//注释也被称为单行注释,/注释通常被称为多行注释。请记住,即使是函数的结尾也不能作为/注释的结尾,必须加上注释结尾标记*/,否则将出现编译错误。
问:什么情况下需要命令行参数?
答:需要让用户修改程序的行为时。例如,Linux命令ls和Windows命令dir都显示当前目录(文件夹)的内容,要查看另一个目录中的文件,需要使用命令行参数指定相应的路径,如ls/或dir\。
2.8 作业
作业包括测验和练习,前者帮助读者加深对所学知识的理解,后者提供了使用新学知识的机会。请尽量先完成测验和练习题,然后再对照附录D的答案。在继续学习下一章前,请务必弄懂这些答案。
1.声明 Int main()有何问题?
2.注释可以超过一行吗?
1.查错:输入下面的程序并编译它。它为什么不能通过编译?如何修复?
2.修复练习1中的错误,然后重新编译、链接并运行它。
3.修改程序清单2.4,以演示减法(使用-)和乘法(使用*)。