[C++ Primer] 第3章: 字符串, 向量和数组

时间:2023-03-10 05:39:10
[C++ Primer] 第3章: 字符串, 向量和数组

标准库类型string

string初始化

string s2(s1);
string s2 = s1;
string s3("value");
string s3 = "value";
string s4(n, 'c'); // n个连续的c组成的字符串

读写string对象

读写未知数量的string对象

int main()
{
string word;
while(cin >> word)
cout << word << endl;
return 0;
}

使用getline读取一整行

int main()
{
string line;
while(getline(cin, line)) // line不含换行符, 第二个参数必须是string类型
cout << line << endl;
return 0;
}

size()返回类型为string::size_type, 推荐使用auto或者decltype推断变量的类型

auto len = line.size();
decltype(line.size()) len;

处理string中的字符

cctype头文件中的函数

函数 说明
isalnum(int c) 测试字符是否为英文或数字,在标准c中相当于使用isalpha(c)或 isdigit(c)
isalpha(int c) 检查参数c是否为英文字母 ,在标准c中相当于使用isupper(c)或islower(c)
isascii(int c) 检查参数c是否为ASCII码字符,也就是判断c的范围是否在0到127之间。
iscntrl(int c) 检查参数c是否为ASCII控制码,也就是判断c的范围是否在0到30之间。
isdigit(int c) 检查参数c是否为阿拉伯数字0到9。
isgraph(int c) 检查参数c是否为可打印字符,若c所对映的ASCII码可打印,且非空格字符则返回TRUE。
islower(int c) 检查参数c是否为小写英文字母。
isprint(int c) 检查参数c是否为可打印字符,若c所对映的ASCII码可打印,其中包含空格字符,则返回TRUE。
isspace(int c) 检查参数c是否为空格字符,也就是判断是否为空格('')、定位字符('\t')、CR('\r')、换行('\n')、垂直定位字符('\v')或翻页('\f')的情况。
ispunct(int c) 检查参数c是否为标点符号或特殊符号。返回TRUE也就是代表参数c为非空格、非数字和非英文字母。
isupper(int c) 检查参数c是否为大写英文字母。
isxdigit(int c) 检查参数c是否为16进制数字,只要c为下列其中一个情况则返回TRUE。16进制数字:0123456789ABCDEF。
tolower(int c) 如果c是大写字母则转成小写, 否则原样输出c
toupper(int c) 如果c是小写字母则转成大写, 否则原样输出c

使用范围for循环处理每个字符

string s("Hello World!");
for(auto &c : s)
c = toupper(c); // c是一个引用, 因此赋值语句可以改变s中字符的值, 若无需改变s中的字符可以不使用引用
cout << s << endl;

注意: 当对字符串s使用下标操作单个字符时, 一般需要先判断s.empty()

标准库类型vector

定义和初始化

vector<T> v1;
vector<T> v2 = v1;
vector<T> v2(v1);
vector<T> v3(n, val); // 创建制定数量的元素, 包含n个重复元素, 每个元素的值都为val
vector<T> v4(n); // 包含n个重复的执行值初始化, 内置类型如int初始化为0
vector<T> v5{a, b, c};// C++11支持
vector<T> v5 = {a, b, c};

重要操作

v.empty()
v.size() // 返回类型为 std::vector<T>::size_type
v.push_back()
v1 = v2; // 用v2元素拷贝替换v1中元素
v1 = {a, b, c...}; // 用列表中元素拷贝替换v1中元素
v1 == v2; v1 != v2; // 其他逻辑运算符同理

注意: 只能对已存在的元素执行下标操作, 尽量使用范围for循环可以有效确保下标合法

迭代器介绍

所有标准库容器都可以使用迭代器(string对象不属于容器类型, 但也支持迭代器). 通过begin和end成员获取迭代器.

标准容器迭代器的运算符

*iter           // 解引用, 返回迭代器所指元素的引用
iter->mem // 等价(*iter).mem
++iter
--iter
iter1 == iter2
iter1 != iter2

所有标准库容器的迭代器都定义了==和!=, 但是他们中的大多数都没有定义<运算符. 因此要养成使用迭代器和!=的习惯, 就不用太在意用的到底是那种容器.

迭代器类型: 使用iterator和const_iterator表示迭代器类型

vector<int>::iterator it;        // 可读写
string::iterator it2;
vector<int>::const_iterator it3; // 只读
string::const_iterator it4;

若对象是常量, 则begin和end返回const_iterator, 否则返回iterator. 无论对象本身是否是常量, cbegin和cend都返回const_iterator.

注意: 凡是使用了迭代器的循环体, 都不要向迭代器所属的容器添加元素.

vector和string迭代器额外支持的运算符

iter + n, iter - n
iter += n, iter -= n
iter1 - iter2 // 类型为difference_type的带符号整数
>, >=, <, <=

数组

使用数据下标的时候, 通常将其定义为size_t类型. size_t类型是一种与机器相关的如符号类型, 它被设计的足够大以便能表示内存中任意对象的大小. 在cstddef头文件中定义了size_t类型.

指针也是迭代器

vector和string的迭代器支持的运算, 数组的指针全都支持, 为了获取尾后指针, C++11新标准引入两个名为begin和end的函数, 这两个函数与容器中的两个同名函数类似. 这两个函数定义在 iterator头文件中.

int ia[] = {1, 2, 3, 4};
int *beg = begin(ia); // 首元素的指针
int *end = end(ia); // 尾后元素指针, 不能执行解引用和递增操作
auto n = end(ia) - begin(ia); // 数组ia中的元素数量, n的类型为ptrdiff_t带符号类型, 该类型定义在头文件cstddef头文件中.

C风格字符串

C标准库String函数, 定义在ctring头文件中

strlen(p)       // 返回p的长度, 不含'\n'
strcmp(p1, p2) // 比较p1, p2相等性, ==返回0, < 返回负值, > 返回正值
strcat(p1, p2) // 将p2附加到p1之后, 返回p1
strcpy(p1, p2) // 将p2拷贝给p1, 返回p1

混用string对象和C风格字符串

string s("Hello World");
const char *str = s.c_str(); // 如果后续操作改变了s, 可能导致str失效, 如果执行完c_str()函数后程序想一直使用返回的数组, 最好将该数组重新拷贝一份.

使用数组初始化vector对象

int iarr[] = {1, 2, 3, 4};
vector<int> ivec(begin(iarr), end(iarr));
vector<int> subVec(iarr + 1, iarr + 3); // 拷贝iarr[1], iarr[2]

多维数组

使用范围for循环处理多维数组, 除了最内层的循环外, 其他所有循环的控制变量都应该是引用类型.

int ia[3][4] = {{0, 1, 2, 3}, {4, 5, 6, 7}, {8, 9, 10, 11}};
// 使用范围for循环
for(auto &raw : ia) // 使用引用类型是为了防止数组名自动转为指针类型
for(auto col : raw) // 如果想要修改数组元素也需要使用引用类型, 单纯输出的话可以不使用引用类型
printf("%d ", col);
// 使用标准库函数
for(auto p = begin(ia); p != end(ia); ++p)
for(auto q = begin(*p); q != end(*p); ++q)
printf("%d ", *q);
// 使用指针
for(int (*p)[4] = ia; p != ia + 3; ++p)
for(int *q = *p; q != *p + 4; ++q)
printf("%d ", *q);