第一章:开始
控制流
读取数量不定的输入数据
此程序实现对用户输入的一组数进行求和,我们预先不知道用户要输入多少个数。
书中代码如下:
int main()
{
int sum = 0;
int val = 0;
while (cin >> val)
{
sum += val;
}
cout << sum<<endl;
system("pause");
}
然而,这种方法需要我们手动指出输入结束,也就是按ctrl-z,或者输入别的数据类型。
改进程序:cin在遇到enter后,才去读取数据。
注意:键盘输入的数据都是先存放在输入缓冲区中的,当键入enter,cin才去缓冲区读取数据。
int main()
{
int sum = 0;
int val = 0;
while (cin >> val)
{
sum += val;
if (getchar() == '\n')
{
break;
}
}
cout << sum<<endl;
system("pause");
}
我们需要用getchar()截取当前的输入,并判断是否是换行,至此完成该程序。
第二章:变量和基本类型
对象:指一块能存储数据并具有某种类型的内存空间。
类型转换:
- 当我们把一个非布尔类型的算术值赋给布尔类型时,初始值为0则结果为false,否则结果为true。
- 当我们把一个布尔值赋给非布尔类型时,初始值为false则结果为0,否则结果为1。
- 当我们把一个浮点数赋值给整数类型时,结果值仅保留浮点数中小数点之前的部分。
- 当我们赋给无符号类型一个超出它表示范围的值时,结果是初始值对无符号类型表示数值总数取模后的余数。例如:8bit的unsigned char表示0-255区间的值,把-1赋给它后所得到的结果是-1对256取模的值255。
- 当我们赋给有符号类型一个超出它表示范围的值时,结果是未定义的。
含无符号类型的表达式:
当一个算术表达式中既有无符号数又有int值时,那个int值会转换成无符号数。
转义序列:
在程序中,转义序列被当作一个字符。
注:nullptr是指针字面值。
变量
列表初始化:
用列表初始化的方法可以检查类型是否不匹配。
默认初始化:
定义在任何函数体之外的变量被初始化为0,函数体内部的内置类型变量不被初始化。
声明和定义:
变量声明规定了变量的类型和名字,在这一点与定义相同。但是,定义还申请了存储空间,并且可能为变量赋一个初始值。
如果想声明一个变量但非定义它,需要加关键字extern,而且不要初始化变量。
extern int i;//声明i
int j;//声明并定义j
extern int k = 1;//定义k
如果在函数体内部初始化一个extern关键字标记的变量会出错。
作用域:
C++中大部分作用域都以花括号分隔。
同一个名字在不同的作用域中可能指向不同的实体。名字的有效区域始于名字的声明语句,以声明语句所在的作用域末端为结束。
二级指针:
通过*的个数可以区分指针的级别。
指向指针的引用:
const限定符:
为了防止程序修改变量的值,我们可以用关键字const对变量的类型加以限定。此外,因为const对象一旦创建后其值就不能再改变,所以const对象必须初始化。
指针常量,本质上一个常量,指针用来说明常量的类型,表示该常量是一个指针类型的常量,常量指针,本质上是一个指针,常量表示指针指向的内容,说明该指针指向一个“常量”。
常量指针(指针地址可以变,值不能变)。
int a = 0,b = 0;
const int *p = &a;
*p = 1; //错误,不可修改常量值
*p = &b; //正确,可以指向另一个常量
指针常量(指针地址不可变,值可变。注:指针常量在定义时要赋初值。)
int a = 0,b = 0;
int* const p = &a;
*p = 1; //正确,可以修改值
*p = &b; //错误,不可改变地址
指向常量的指针常量(指针地址不可变,值不可变)
int a = 0,b = 0;
const int * const p = &a;
*p = 1; //错误,不可以修改值
*p = &b; //错误,不可改变地址
auto类型说明符:
使用auto能让编译器替我们去分析表达式所属的类型。
decltype类型指示符:
它的作用是选择并返回操作数的数据类型。在此过程中,编译器分析表达式并得到它的类型,却不实际计算表达式的值。
// sum的类型就是函数f返回的类型
decltype(f()) sum = x;
decltype并不会实际计算表达式的值,编译器分析表达式并得到它的类型。
函数调用也算一种表达式,因此不必担心在使用decltype时真正的执行了函数,正如前例中的f()。
预处理器:
确保头文件多次包含仍能安全工作的常用技术是预处理器。当预处理器看到#include标记时就会用指定的头文件的内容代替#include。
C++还会用的一项预处理功能是头文件保护符。预处理变量有两种状态:已定义和未定义。#define指令把一个名字设定为预处理变量,另外两个指令则分别检查某个指定的预处理变量是否已经定义。
#ifdef 当且仅当变量已定义时为真,#ifndef当且仅当变量未定义时为真。一旦检查结果为真,则执行后续操作直到遇到#endif指令为止。
例如:
#ifndef SALES_DATA_H
#define SALES_DATA_H
#include <string.h>
struct Sales_data
{
std::string bookNo;
unsigned units_ssold = 0;
double revenue = 0.0;
};
#endif
第一次包含Sales_data.h时,#ifndef为真,预处理器将顺序执行直到遇到#endif为止。此时,预处理变量SALES_DATA_H也变成已定义,后续如果再包含一次,则#ifndef检查结果为假,不再执行。
第三章:字符串、向量和数组
字符串string
初始化string对象的方式
string对象上的操作
1.读写:cin cout
注意:cin遇到空格就结束读入。
2.读取未知数量的string对象
注意:string遇见空格就停止读入,因此需要先输出再检测换行符,否则最后一个字符串无法输出。
3.使用getline读取一整行
我们希望在最终得到的字符串中保留输入时候的空白符,因此需要getline函数。该函数从给定的输入流中读取内容,直到遇到换行符为止。
4.处理string对象中的字符
我们需要单独处理string对象中的字符,例如检查一个string对象是否包含空白,或者把string对象的字母全部改为小写等。在这些问题中,我们需要知道字符的特性,在cctype中定义了一组标准库函数处理这些问题。
5.处理每个字符,使用基于范围的for语句。
for(declaration:expression)
statement
declaration负责定义一个变量,该变量用于访问序列中的基础元素。expression是一个对象,表示一个序列。每次迭代,declaration的变量会被初始化为expression的下一个元素值。
举个例子:
此外,如果想要改变string对象中字符的值,必须把循环变量定义为引用类型。假设我们想要把字符串改为大写字母的形式,代码如下:
其中使用了标准库函数toupper,该函数接收一个字符,然后输出其对应的大写形式。
vector
定义和初始化vector对象
C++11中的列表初始化vector对象
例如:vector<string> articles = { "a","aaa","the" };
创建指定数量的元素
向vector对象中添加元素
注:vector容器中的下标运算符只可用于访问已存在的元素,不能用于添加元素。添加元素使用push_back
迭代器
所有迭代器都拥有begin和end两个成员。
begin指向第一个元素。
end指向尾元素的下一个元素。
迭代器的使用
例如:我们需要把string对象的第一个字符改成大写。
如果只需要读操作而无需写操作的话,C++11提供了两个新函数,cbegin和cend。
数组
理解复杂的数组声明
数组本身就是对象,因此允许定义数组的指针以及数组的引用。
例如:int *(&arr)[10] = arrs
按照由内向外的顺序,我们可以首先看出(&arr)说明arr是一个引用,再看右边知道,arr引用对象是一个大小为10的数组,观察左边发现,数组存放的类型是int *。因此,arr引用的是一个存放10个整型指针的数组。
指针和数组
在大多数表达式中,使用数组类型的对象其实是使用一个指向该数组首元素的指针。
例如:string *p = &s[0];
和 string *p = s;
两条语句等价。
C++11引入了begin和end函数用来得到数组的首和尾后指针。
用法如下:
接下来就可以用和容器一样的遍历方法遍历数组了。
此外,我们可以用auto n = end(arr) = begin(arr)
得到arr的长度。
使用数组初始化vector
只需要指明要拷贝区域的首元素地址和尾元素地址就行。
多维数组
例如:int arr[10][20][30] = {0};
该数组大小为10,它的每个元素都是大小为20的数组,这些数组的元素是含有30个整数的数组。
使用范围for语句处理多维数组。
注意:用for来处理多维数组,除了最内层的循环,其他所有的循环控制变量都应该是引用类型。
第一个循环遍历arr的所有元素,这些元素是大小为3的数组,因此p就是含有3个整数的数组的引用。第二个循环遍历那些4元素数组中的某一个。
如果最外层循环不是引用类型,数组会被自动转成指针,内层循环就不合法了。
我们可以推断出p是指向含有4个整数的数组的指针,q的类型是指向整数的指针。