读Essential C++ 第一章总结
c++程序语言的基本组成,其中包括:
- 一些基本数据类型:布尔值(Boolean)、字符(charater)、整数(integer)、浮点数(floating poin).
- 算术运算符、关系运算符以及逻辑运算符,用以操作上述基础数据类型。
- 条件分支和循环控制语句,例如if语句和while循环,可用来改变程序的控制流程。
- 一些复合类型,例如指针及数组。指针可以让我们间接参考一个已存在的对象,数组则用来定义一组具有相同数据类型的元素。
- 一套标准的、通用的抽象化库,例如字符串和向量(vector).
1.1 如何撰写C++程序
关键字(keyword),就是程序语言预先定义的一些具有特殊意义的名称。int用来表示语言内置的整数数据类型。
函数(function) 是一块独立的程序代码序列(code sequence),能够执行一些运算。它包含四个部分:返回值类型(return type)、函数名称、参数列表(parameter list),以及函数体(function body)。
函数的返回值通常用来表示运算结果。
函数名称是由程序员选定的。函数名最好能够提供某些信息,让我们容易了解函数实上在做些什么。
参数列表由两个括号括住,置于函数名之后。空的参数列表,表示不接受任何参数。
函数的的主体由大括号({})标出,其中含有“提供此函数之运算”的程序代码。双斜线表示该行内容为注释,也就是程序员对程序代码所做的某些说明。
所谓类(class),是用户自定义的数据类型(user-define data type)。class机制让我们得以将数据类型加入我们的程序中,并有能力识别他们。面向对象的类层次体系(class hierarchy)定义了整个家族体系的各相关类型,例如终端与文件输入设备、终端与文件输出设备等。
class机制,赋予了我们“增加程序内之类型抽象化层次”的能力。
class的定义,一般来说分为两部分,分别写在不同的文件中。一是头文件(header file), 用来声明该class所提供的各种操作行为(operation)。二是程序代码文件,则包含了这些操作行为的实现内容(implementation)。
所谓字符常量(character literal) 系由一组单引号括住。字符常量分为两类:第一类是可打印字符,例如英文字母('a'、‘A’,等等)、数字、标点符号(';'、'-',等等)。另一类是不可打印字符,例如换行符('\n') 或制表符(tab, '\t')。由于不可打印字符并无直接的表示法,所以必须以两个字符所组成的字符序列来表示。
所谓命令空间(namespace) 是一种将库名称封装起来的方法。通过这种方法,可以避免和应用程序发生命令冲突的问题(所谓命名冲突是指在应用程序内两个不同的实体(entity)具有相同名称,导致程序无法区分两者。命名冲突发生时,程序必须等待该命名冲突获得解析之后,才得以继续执行)。
using namespace std;
1.2 对象的定义与初始化
对象名称的命名可以是任何字母、数字、下划线的组合。区分大小写。对象名称不能以数字开头。
对整数对象进行初始化:
int num = 0;
使用构造函数语法进行初始化:
int num(0);
1.3 撰写表达式
内置数据类型都可运用这样的一组运算符,其中包括算符运算符、关系运算符、逻辑运算符、复合赋值(compound assignment) 运算符。
复合赋值运算符可以和每个算符运算符结合,形成+=、-=、*=、/=和%=。
任何关系运算符(relational operator) 的求值结果不是true就是false。
运算符的优先级:
位置在上者的优先级高于位置在下者。同一行的各种运算符具有相同的优先级,其求值次序取决于它在该表达式中的位置(由左至右)。
逻辑运算符NOT
算符运算符(*, /, %)
算术运算符(+,-)
关系运算符(<, >, <=, >=)
关系运算符(==, !=)
逻辑运算符AND
逻辑运算符OR
赋值运算符=
1.4 条件语句和循环语句
1.5 如何运用Array 和Vector
使用可存放连续整数值得容器(container)类型。
一般而言,建议使用vector甚于array.
要定义array,必须指定array的元素类型,赋予一个名称,并指定其尺度大小。array的大小必须是一个常量表达式(cpmstant expression).
const int seq_size = 18;
int pell_seq[seq_size];
定义vector对象,必须包含vector头文件。vector是个class template。必须在雷鸣之后的尖括号内指定其元素类型,其大小则写在小括号中,此处的大小不一定是常量表达式,也可以不指定大小。
#include <vector>
std::vector<int> pell_seq(seq_size);
1.6 指针带来弹性
指针具有双重性质:既可以然我们操作指针包含的内存地址,也可以让我们操作指针所指的对象值。
int ival = 1024;
int *pi = &ival;
指针可能不指向任何对象。当我们写*pi时,这种写法可能会(也可能不会)使程序在运行时产生错误。如果pi定位到某个对象,则对pi进行解引用操作没有错误。但如果pi不指向任何对象,则解引用pi会导致未知的执行结果。这就意味着在使用指针时,必须先对指针进行判空。
1.7 文件的读写
对文件进行读写操作,首先得包含头文件 fstream:
#include <fstream>
打开一个可供输出的文件,需要定义ofstream (供输出用的file stream)对象,并将文件名传入:
// 以输出模式开启test.txt
ofstream outfile("test.txt");
注意:声明outfile的同时,如果指定的文件并不存在,便会有一个文件被产生出来并打开供输出使用。如果指定的文件已经存在,这个文件会被打开用于输出,而文件中原有的数据会被丢弃掉。
如果文件已经存在,但我们并不希望丢弃其原有内容,而是希望将新数据增加到该文件中,那么我们必须以追加模式(append mode)打开这个文件。因此,需要提供第二个参数 ios_base::app给ofstream对象。
//以追加模式(append mode)打开test.txt
//新数据会被加到文件末尾
ofstream outfile("test.txt", ios_base::app);
//如果outfile的求值结果为false,表示此文件并未成功打开
if(!outfile) {
// 因为某种原因,无法开启
cerr << "Oops! Unable to save session date!\n";
}
cerr代表标准错误设备(standard error)。和cout一样,cerr将其输出结果定向到用户的终端。两者的唯一差别是,cerr的输出结果并无缓冲(buffered)情形--它会立即显示于用户终端中。
如果要打开一个可供读取的文件,需要定义一个ifstream(file stream)对象,并将文件名传入。如果文件未能成功打开,该ifstream对象就为false.如果成功,该文件的读取位置会被设定在起始位置。
// 以读取模式(input mode)打开infile
ifstream infile("test.txt");
int num_tries = 0;
int num_cor = 0;
if (!infile) {
//由于某种原因,文件无法打开...
//我们将假设这是一位新的用户...
} else {
// ok: 读取档案中的每一行
// 每一行的格式是:
// name num_tries num_correct
// nt: 猜过的总次数(num_tries)
// nc: 猜对的总次数(num_correct)
string name;
int nt;
int nc;
while (infile >> name) {
infile >> nt >> nc;
if (name == usr_name) {
// 找到了
cout << "Welcome back, " << usr_name
<< "\nYour current score is " << nc
<< " out of " << nt << "\nGood Luck!\n"
num_tries = nt;
num_cor = nc;
}
}
}
while循环的每次迭代都会读取文件的下一行内容。这样的操作会持续到文件末才结束。
如果同时读写同一个文件,需要定义一个fstream对象。为了以追加模式(append mode)打开,需要传入第二参数值ios_base::in|iosbase::app;
fstream iofile("test.txt", ios_base::in|ios_base::app);
if (!iofile) {
// 由于某种原因,文件无法打开...
} else {
// 开始读取之前,将文件重新定位至起始处
iofile.seekg(0);
....
}
当我们以追加模式来打开文档,文件位置会位于末尾。如果没有先重新定位,就试着读取文件内容,那么立刻就会遇上“文件结束”的状况。seekg()可将iofile重新定位至文件的起始处。由于此文件是以追加模式开启,因此任何写入操作都会将数据添加在文件末尾。