1 基本概念
1.1 主函数
每个 C++ 程序都包含一个或多个函数,其中一个必须命名为main
,称为主函数。操作系统通过调用main
来运行 C++ 程序。
一个函数的定义包含四个部分:返回类型、函数名、一个括号包围的形参列表、函数体。虽然main
函数在某种程度上比较特殊,但其定义与其它函数是一样的。下面是一个非常简单的main
函数,它什么也不干,只是返回给操作系统一个值:
int main()
{
return 0;
}
main
函数的返回类型必须为int
,即整数类型。int
类型是一种内置类型(built-in type),即语言自身定义的类型。类型是程序设计最基本的概念之一,一种类型不仅定义了数据元素的内容,还定义了这类数据上可以进行的运算。程序所处理的数据都保存在变量中,而每个变量都有自己的类型。
函数体是一个以左花括号{
开始,以右花括号}
结束的语句块。这个语句块中唯一的一条语句是return
,它结束函数的执行,并且向调用者返回一个值。当return
语句包括一个值时,此返回值的类型必须与函数的返回类型相容。
大多数 C++ 语句以分号;
表示结束。
在大多数系统中,main
的返回值被用来指示状态。返回值 0 表明成功,非 0 的返回值的含义由系统定义,通常用来指出错误类型。
1.2 头文件
每个使用标准库的程序都必须导入相关的头文件,通过#include
指令导入。当预处理器看到#include
标记时就会用指定的头文件的内容代替#include
。
1.2.1 建议使用 C++ 版本的 C 标准库头文件
C++ 标准库中除了定义 C++ 语言特有的功能外,也兼容了 C 语言的标准库。C 语言的头文件形如name.h
,C++ 则将这些文件命名为cname
。也就是去掉了.h
后缀,而在文件名name
之前添加了字母c
,这里的c
表示这是一个属于 C 语言标准库的头文件。
两者的内容是一样的,只不过从命名规范上来讲更符合 C++ 语言的要求。特别的,在名为cname
的头文件中定义的名字从属于命名空间std
,而定义在名为.h
的头文件中的则不然。
一般来说,C++ 程序应该使用名为cname
的头文件而不使用name.h
的形式,标准库中的名字总能在命名空间std
中找到。如果使用.h
形式的头文件,程序员就不得不时刻牢记哪些是从 C 语言继承过来的,哪些又是 C++ 语言所独有的。
1.2.2 编写自己的头文件
为了确保各个文件中类的定义一致,类通常被定义在头文件中,而且类所在头文件的名字应与类的名字一样。
头文件通常包含那些只能被定义一次的实体,如类、const
和constexpr
变量等。头文件也经常用到其它头文件的功能。
1.2.3 预处理器概述
确保头文件多次包含仍能安全工作的常用技术是预处理器,它由 C++ 从 C 语言继承而来。预处理是在编译之前执行的一段程序,可以部分地改变我们所写的程序。
C++ 程序还会用到的一项预处理功能是头文件保护符,头文件保护符依赖于预处理变量。预处理变量有两种状态:已定义和未定义。#define
指令把一个名字设定为预处理变量,另外两个指令则分别检查某个指定的预处理变量是否已经定义:#ifdef
当且仅当变量已定义时为真,#ifndef
当且仅当变量未定义时为真。一旦检查结果为真,则执行后续操作直至遇到#endif
指令为止。预处理变量无视 C++ 语言中关于作用域的规则。
整个程序中的预处理变量包括头文件保护符必须唯一,通常的做法是基于头文件中类的名字来构建保护符的名字,以确保其唯一性。
1.3 输入输出流
C++ 并未定义任何输入输出(IO)语句,而是通过标准库iostream
来提供 IO 机制(以及很多其它设施)。iostream
库包含两个基础类型istream
和ostream
,分别表示输入流和输出流。一个流就是一个字符序列,是从 IO 设备读出或写入 IO 设备的。术语“流”想要表达的是,随着时间的推移,字符是顺序生成或消耗的。
1.3.1 标准输入输出对象
标准库定义了 4 个 IO 对象:(1)cin
,称为标准输入的istream
类型的对象;(2)cout
,称为标准输出的ostream
类型的对象;(3)cerr
,称为标准错误的ostream
类型的对象,用于输出警告和错误消息;(4)clog
,ostream
类型的对象,用于输出程序运行时的一般性信息。
系统通常将程序所运行的窗口与这些对象关联起来。
1.3.2 向流写入和读取数据
输出运算符<<
接受两个运算对象:左侧的运算对象必须是一个ostream
对象,右侧的运算对象是要打印的值。此运算符将给定的值写到给定的ostream
对象中。输出运算符的计算结果就是其左侧运算对象,因此多个输出运算符的输出请求可以连接起来。
std::endl
是一个被称为操纵符的特殊值,效果是结束当前行,并将与设备关联的缓冲区中的内容刷新到设备中。缓冲刷新操作可以保证目前为止程序所产生的所有输出都真正写入输出流中,而不是仅仅停留在内存中等待写入流。
输入运算符>>
与之类似,接受一个istream
作为其左侧运算对象,接受一个对象作为其右侧运算对象。它从给定的istream
读入数据,并存入给定对象中。并也返回其左侧运算对象作为其计算结果,因此也可以将一系列输入请求连接到一起。
例如,以下示例要求输入两个整数,然后程序会输出两个整数的和:
#include <iostream>
int main() {
int a = 0, b = 0;
std::cout << "Enter two numbers: " << std::endl;
std::cin >> a >> b;
std::cout << "The sum of " << a << " and " << b << " is " << a + b << std::endl;
return 0;
}
假设我们要输入的整数是 1 和 2,则程序运行结果为:
Enter two numbers:
1 2
The sum of 1 and 2 is 3
标准库定义了不同版本的输入输出运算符,因此可以在一行语句中处理这些不同类型的运算对象。
1.4 命名空间
命名空间可以帮助我们避免不经意的名字定义冲突,以及使用库中相同名字导致的冲突。标准库定义的所有名字都在命名空间std
中,例如std::cin
表示从标准输入中读取内容。
通过命名空间使用标准库有一个副作用,就是当使用标准库中的一个名字时,必须显式通过作用域运算符(::
)来说明我们想使用来自命名空间中的名字。
我们可以通过使用using
声明,则无须专门的前缀也能使用所需的名字。其形式为:using namespace::name;
。按照规定,每个using
声明引入命名空间中的一个成员,要用到的标准库中的名字都需要一条using
声明语句。
位于头文件的代码一般来说不应该使用using
声明。这是因为头文件的内容会拷贝到所有引用它的文件中去,如果头文件里有某个using
声明,那么每个使用了该头文件的文件就都有这个声明。对于某些程序来说,由于不经意间包含了一些名字,反而可能产生始料未及的名字冲突。
2 实用工具
2.1 注释
编译器会忽略注释,因此注释对程序的行为或性能不会有任何影响。
C++ 有两种注释:单行注释和界定符对注释。单行注释以双斜线//
开始,以换行符结束,可以包含任何文本。界定符对注释继承自 C 语言,以/*
开始,*/
结束,中间可以包含除*/
以外的内容,包括换行符。界定符对注释不能嵌套。