C++Primer笔记-字符串、向量和数组

前言

该系列是《C++Primer第五版》的笔记,包含本人认为值得记录和整理的主要的知识点,并不是全部内容,也不是具体的内容。
该系列文章的作用应该是作为复习或预习的参考,有哪些知识点忘记或想学,可以大致浏览下该文章,然后再去书中寻找详细解答。(本系列文章基本是按书本顺序罗列的知识点,便于大家去书中寻找)
所以看该文章前,需要有一定的C++基础,否则阅读起来可能有困难。

本文大致整理了第三章的知识点,属于C++比较基础的内容,没有难点,只有内置数组和指针的搭配使用起来比较麻烦。

链接目录

命名空间的using声明

声明形式:using namespace::name
例如,如果不进行命名空间声明,对于标准输入输出,需要加上std::

//如果没有声明命名空间,需要显式注明
std::cout << "hello world" << std::endl;
//声明命名空间后,可以省去显式注明
using std::cout;
using std::endl;
cout << "hello world" << endl;

//如果是导入整个命名空间,则需要添加namespace
using namespace std;
cout << "hello world" << endl;

关于命名空间在十八章有更详细的介绍。

标准库类型string

定义在头文件string中,包含在命名空间std中。

多种初始化方式:

  • 默认初始化为空字符串:string s;
  • 初始化为字符串:string s = "hello";
  • 拷贝初始化:string s(str);,s的内容从str那拷贝
  • 带参初始化:string s = string(10, 'c');,内容包含10个c

注意string.size()返回的是string::size_type类型的值,是一种无符号数,如果和有符号数混合使用,可能会有预期外的错误。

例如:s.size() < n,如果n是一个负数,看作无符号数则是一个非常大的数,使该条件判断几乎永远为真。

关于string对象的各种操作在书中有表格罗列,本文就不再花费篇幅进行罗列了。

标准库类型vector

vector是一种容器,类似于动态数组,定义在头文件vector中,包含在命名空间std中。

多种初始化方式:其中T为数据类型

  • 默认初始化为空数组:vector<T> v;
  • 拷贝初始化:vector<T> v(v2),v中内容为v2的拷贝
  • 带参初始化:vector<T> v(n, val);,v中包含n个值为val的元素
  • 列表初始化:vector<T> v{a, b, c};,v中包含a、b、c

vector添加元素:v.push_back(val);,在v的末尾添加值为val的元素

其他vector操作:

  • v.empty():当v空时为真
  • v.size():返回v中元素个数
  • v[n]:返回第n个位置上元素的引用

迭代器

迭代器的使用类似于指针,但实际上是一种标准库内置的类。

对于有迭代器的类型,都包含beginend成员,其中begin指向第一个元素,end指向尾后元素(实际上不存在内容,仅是个标记)。
例如上述的vector:获取v的迭代器auto b = v.begin(), e = v.end();

迭代器运算:

  • *iter:返回迭代器iter所指元素的引用
  • iter->mem:等价于(*iter).mem
  • iter++:指向下一元素
  • iter+n:向后移动n个元素,可能越界
  • iter--:指向上一元素
  • iter-n:向前移动n个元素,可能越界
  • iter1 - iter2:迭代器所指元素的距离
  • iter1 == iter2:当指向相同元素时为真

上述auto可能会让有些人感到困惑,想明白迭代器的具体类型是什么。实际上和引入auto的初衷一样,我们通常知道如何使用一个迭代器,但对于迭代器的准确类型,有时候是不清晰的。
例如,对于一个vector<int>的迭代器,其准确类型是vector<int>::iterator,这样对于编写程序不方便,我们通常只需要知道怎么使用它们即可,无需明白它们的具体类型。

数组

对于C++的内置数组,数组大小和维度的参数必须是常量表达式,即在编译期间就能确定的参数。
对于所声明数组的元素的初始化操作,和内置类型的变量初始化一致(本系列第二章有提及

多种初始化方式:

  • 默认初始化:int arr[10];
  • 列表初始化:int arr[3] = {1, 2, 3};
  • 忽略参数的列表初始化:int arr[] = {1, 2, 3};编译器会自动判断数组大小
  • 缺少元素的列表初始化:int arr[5] = {1, 2, 3};剩下元素执行默认初始化

关于字符数组的特殊情况,我们知道C++中字符串以\0结尾,代表字符串的结束,所以需要多一个位置给\0
于是有以下特殊情况:

  • char arr[] = "C++";,会自动添加\0在结尾
  • char arr[3] = "C++";,错误,没有空间存放\0
  • char arr[4] = {'C', '+', '+', '\0'};,手动添加\0

另外,数组不允许拷贝和赋值,int arr2[] = arr;将是错误的。

复杂数组声明,这里涉及到表达式的优先级,在下一章有详细介绍。

int *pt[10];//含有10个整型指针的数组
int (*ptr)[10] = &arr;//ptr指向一个含有10个整型元素的数组
int (&arrRef)[10] = arr;//arrRef引用一个含有10个整型元素的数组

指针和数组:在大多数表达式中,数组名实际上会被隐式的转化为指向数组首元素的指针。

int arr[] = {1, 2, 3};
int *p = arr;//p指向arr首元素
p++;//p向后移动一个元素

数组的下标运算[]:实际上是对指针进行解运算

int arr[] = {1, 2, 3};
int *p = arr + 2;//指向尾元素
int i = p[-1];//等价于*(p-1),此时指向第二个元素

现代C++程序除了特殊场景外,应该尽量使用vector而不是内置数组。

C风格字符串

由于历史原因,C++兼容C的代码,所以需要兼顾C风格的字符串,即char数组类型,这里需要注意\0和数组大小。

//例如
char str[] = {'C', '+', '+'};
//由于str没有空字符结尾,可能会一直沿着内存空间寻找空字符
cout << strlen(str) << endl;//strlen用来计算字符数组的长度
//以下情况必须保证largeStr必须有足够大的空间,否则会引发严重错误
strcpy(largeStr, s);//把s拷贝给largeStr
strcat(largeStr, s);//把s连接到largeStr后面

多维数组

多维数组的初始化:

int arr[3][3] = {
	{0, 1, 2},
	{3, 4, 5},
	{6, 7, 8}
};
//等价于
int arr[3][3] = {0, 1, 2, 3, 4, 5, 6, 7, 8};
//对于缺值的初始化,每行未定义的值为初始值
int arr[3][3] = {
	{1},
	{2},
	{3}
};
int arr[3][3] = {1, 2, 3};//对于该种缺值的初始化,只有第一行被定义了

指针和多维数组:

int arr[3][3];
int (*p)[3] = arr;//p指向含有三个元素的数组
p = &arr[2];//p指向arr的第三个元素(第三行)

//另外,圆括号不可省略
int *p[4];//含有四个整型指针的数组
int (*p)[4];//指向含有四个元素的数组的指针
上一篇 下一篇

评论 | 0条评论