主页
文章
分类
系列
标签
简历
【C++ Primer(edition 5) 03】字符串、向量和数组
发布于: 2021-9-8   更新于: 2021-9-8   收录于: Cpp
文章字数: 466   阅读时间: 3 分钟   阅读量:

一、string

  • 标准库类型string表示可变长的字符序列。
  • #include <string>,然后 using std::string;
  • string对象:注意,不同于字符串字面值。

1、定义和初始化string对象

定义string对象的方式:

方式 解释
string s1 默认初始化,s1是个空字符串
string s2(s1) s2s1的副本
string s2 = s1 等价于s2(s1)s2s1的副本
string s3("value") s3是字面值“value”的副本,除了字面值最后的那个空字符外
string s3 = "value" 等价于s3("value")s3是字面值"value"的副本
string s4(n, 'c') s4初始化为由连续n个字符c组成的串

[[顺序容器#^5f2418|构造string的其他方法]]

  • 拷贝初始化(copy initialization):使用等号=将一个已有的对象拷贝到正在创建的对象。
  • 直接初始化(direct initialization):通过括号给对象赋值。

2、string对象上的操作

string的操作:

操作 解释
os << s s写到输出流os当中,返回os
is >> s is中读取字符串赋给s,字符串以空白分割,返回is
getline(is, s) is中读取一行赋给s,返回is
s.empty() s为空返回true,否则返回false
s.size() 返回s中字符的个数
s[n] 返回s中第n个字符的引用,位置n从0计起
s1+s2 返回s1s2连接后的结果
s1=s2 s2的副本代替s1中原来的字符
s1==s2 如果s1s2中所含的字符完全一样,则它们相等;string对象的相等性判断对字母的大小写敏感
s1!=s2 同上
<, <=, >, >= 利用字符在字典中的顺序进行比较,且对字母的大小写敏感(对第一个不相同的位置进行比较)
  • string io:
    • 执行读操作>>:忽略掉开头的空白(包括空格、换行符和制表符),直到遇到下一处空白为止。
    • getline:读取一整行,包括空白符
  • s.size()返回的时string::size_type类型,记住是一个无符号类型的值,不要和int混用
  • s1+s2使用时,保证至少一侧是string类型。string s1 = "hello" + "world" // 错误,两侧均为字符串字面值
  • 字符串字面值和string是不同的类型。

3、处理string对象中的字符

  • ctype.h vs. cctype:C++修改了c的标准库,名称为去掉.h,前面加c

    如c++版本为cctype,c版本为ctype.h

    • 尽量使用c++版本的头文件,即cctype

cctype头文件中定义了一组标准函数:

函数 解释
isalnum(c) c是字母或数字时为真
isalpha(c) c是字母时为真
iscntrl(c) c是控制字符时为真
isdigit(c) c是数字时为真
isgraph(c) c不是空格但可以打印时为真
islower(c) c是小写字母时为真
isprint(c) c是可打印字符时为真
ispunct(c) c是标点符号时为真
isspace(c) c是空白时为真(空格、横向制表符、纵向制表符、回车符、换行符、进纸符)
isupper(c) c是大写字母时为真
isxdigit(c) c是十六进制数字时为真
tolower(c) c是大写字母,输出对应的小写字母;否则原样输出c
toupper(c) c是小写字母,输出对应的大写字母;否则原样输出c
  • 遍历字符串:使用范围for(range for)语句: for (auto c: str),或者 for (auto &c: str)使用引用直接改变字符串中的字符。 (C++11)
  • str[x],[]输入参数为string::size_type类型,给出int整型也会自动转化为该类型

二、vector

[[C++Primer#第九章|深层次的介绍]]

  • vector是一个容器,也是一个类模板;
  • #include <vector> 然后 using std::vector;
  • 容器:包含其他对象。
  • 类模板:本身不是类,但可以实例化instantiation(编译器根据模板创建类或函数的过程称为实例化)出一个类。 vector是一个模板, vector<int>是一个类型。
  • 通过将类型放在类模板名称后面的尖括号中来指定类型,如vector<int> ivec

1、定义和初始化vector对象

初始化vector对象的方法

方法 解释
vector<T> v1 v1是一个空vector,它潜在的元素是T类型的,执行默认初始化
vector<T> v2(v1) v2中包含有v1所有元素的副本
vector<T> v2 = v1 等价于v2(v1)v2中包含v1所有元素的副本
vector<T> v3(n, val) v3包含了n个重复的元素,每个元素的值都是val
vector<T> v4(n) v4包含了n个重复地执行了值初始化的对象
vector<T> v5{a, b, c...} v5包含了初始值个数的元素,每个元素被赋予相应的初始值
vector<T> v5={a, b, c...} 等价于v5{a, b, c...}
  • 列表初始化: vector<string> v{"a", "an", "the"}; (C++11)
  • 元素由内默认初始化有两个限制,其一有些类必须明确的提供初始值,其二,如果只提供了元素的数量,而没有设定初始值,只能使用直接初始化。
  • 如果初始化时使用了花括号的形式,但是提供了值又不能用来列表初始化就要考虑用这样的值来构造vector对象。

2、向vector对象中添加元素

  • v.push_back(e) 在尾部增加元素。

3、其他vector操作

vector支持的操作:

操作 解释
v.emtpy() 如果v不含有任何元素,返回真;否则返回假
v.size() 返回v中元素的个数
v.push_back(t) v的尾端添加一个值为t的元素
v[n] 返回v中第n个位置上元素的引用
v1 = v2 v2中的元素拷贝替换v1中的元素
v1 = {a,b,c...} 用列表中元素的拷贝替换v1中的元素
v1 == v2 v1v2相等当且仅当它们的元素数量相同且对应位置的元素值都相同
v1 != v2 同上
<,<=,>, >= 以字典顺序进行比较
  • 范围for语句内不应该改变其遍历序列的大小。
  • vector对象(以及string对象)的下标运算符,只能对确知已存在的元素执行下标操作,不能用于添加元素。

三、迭代器iterator

  • 所有标准库容器都可以使用迭代器。
  • 类似于指针类型,迭代器也提供了对对象的间接访问。

1、使用迭代器

  • vector<int>::iterator iter
  • auto b = v.begin();返回指向第一个元素的迭代器。
  • auto e = v.end();返回指向最后一个元素的下一个(哨兵,尾后,one past the end)的迭代器(off the end)。
  • 如果容器为空, begin()end()返回的是同一个迭代器,都是尾后迭代器。
  • 使用解引用符*访问迭代器指向的元素。
  • 养成使用迭代器和!=的习惯(泛型编程)。
  • 容器:可以包含其他对象;但所有的对象必须类型相同。
  • 迭代器(iterator):每种标准容器都有自己的迭代器。C++倾向于用迭代器而不是下标遍历元素。
  • const_iterator:只能读取容器内元素不能改变。
  • 箭头运算符: 解引用 + 成员访问,it->mem等价于 (*it).mem
  • 谨记:但凡是使用了迭代器的循环体,都不要向迭代器所属的容器添加元素

标准容器迭代器的运算符:

运算符 解释
*iter 返回迭代器iter所指向的元素的引用
iter->mem 等价于(*iter).mem
++iter iter指示容器中的下一个元素
--iter iter指示容器中的上一个元素
iter1 == iter2 判断两个迭代器是否相等

2、迭代器运算

vectorstring迭代器支持的运算:

运算符 解释
iter + n 迭代器加上一个整数值仍得到一个迭代器,迭代器指示的新位置和原来相比向前移动了若干个元素。结果迭代器或者指示容器内的一个元素,或者指示容器尾元素的下一位置。
iter - n 迭代器减去一个证书仍得到一个迭代器,迭代器指示的新位置比原来向后移动了若干个元素。结果迭代器或者指向容器内的一个元素,或者指示容器尾元素的下一位置。
iter1 += n 迭代器加法的复合赋值语句,将iter1加n的结果赋给iter1
iter1 -= n 迭代器减法的复合赋值语句,将iter2减n的加过赋给iter1
iter1 - iter2 两个迭代器相减的结果是它们之间的距离,也就是说,将运算符右侧的迭代器向前移动差值个元素后得到左侧的迭代器。参与运算的两个迭代器必须指向的是同一个容器中的元素或者尾元素的下一位置。
>>=<<= 迭代器的关系运算符,如果某迭代器
  • difference_type:保证足够大以存储任何两个迭代器对象间的距离,可正可负。

三、数组

  • 相当于vector的低级版,长度固定
  • 如果不清楚元素的确切个数,请使用vector。

1、定义和初始化内置数组

  • 初始化:char input_buffer[buffer_size];,长度必须是const表达式,或者不写,让编译器自己推断。
  • 数组不允许直接赋值给另一个数组。
  • 字符数组的特殊性 : 字符数组有一种额外的初始化形式,可以用字符串字面值对此类数组初始化。一定要注意字符串字面值的结尾处还有一个空字符
1
2
char a1[] = {'c' , '+', '+'};//列表初始化,没有空字符 
char a2[]="C++" //自动添加表示字符串结束的空字符
  • 理解复杂的数组声明 : 由内向外,从右向左
  • 用数组初始化 vectorint a[] = {1,2,3,4,5}; vector<int> v(begin(a), end(a));

2、访问数组元素

  • 数组下标的类型:size_t
  • 字符数组的特殊性:结尾处有一个空字符,如 char a[] = "hello";

3、数组和指针

  • 使用数组时,编译器一般会把它转换成指针。
  • 标准库类型限定使用的下标必须是无符号类型,而内置的下标可以处理负值。 int *p=a[2] ; int k= p[-2]; //k=a[0]*
  • 指针访问数组:在表达式中使用数组名时,名字会自动转换成指向数组的第一个元素的指针。
  • 当使用数组作为一个auto变量的初始值时,推断得到的类型是指针而非数组。当使用decltype的关键字时,上述转换不会发生。
  • 在宿主中引入beginend函数来获取数组的首元素和数组的尾后元素。
  • 两个指向数组的指针相减的结果是它们之间的距离。 结果类型为ptrdiff_t的标准库类型。

4、C风格字符串

C风格的字符串是一种特殊的字符数组

1
2
const char *c = "Halo,World";
const char c[] = {'H','a','l','o',',','W','o','r','l','d','\0'};

两者等价,请注意,该数组的最后一个字符为空字符\0,这也被称为字符串结束字符,因为它告诉编译器, 字符串到此结束。这种C风格字符串是特殊的字符数组,因为总是在最后一个字符后加上空字符\0,在代码中使用字符串字面量时,编译器将负责在它后面添加\0。 在数组中间插入\0并不会改变数组的长度,而只会导致将该数组作为输入的字符串处理将到这 个位置结束,如果不加\0,则会导致在打印或者计算长度等操作是无法停止或者输出垃圾数据,从而程序奔溃。

C风格字符串不仅使用起来不方便而且极易引发程序漏洞是, 诸多安全问题的根本原因。

  • 从C继承来的字符串。
  • 用空字符结束(\0)。
  • 对大多数应用来说,使用标准库 string比使用C风格字符串更安全、更高效。
  • 获取 string 中的 cstringconst char *str = s.c_str();
1
2
3
char  ca[] = {'C' , '+' , '+'};
cout << strlen(ca)<<endl;
//严重错误CA没有以空字符结束。 Strlen函数将有可能沿着ca在内存中的位置不断向前寻找,直到遇到空字符才停下。

C标准库String函数,定义在<cstring> 中:

函数 介绍
strlen(p) 返回p的长度,空字符不计算在内
strcmp(p1, p2) 比较p1p2的相等性。如果p1==p2,返回0;如果p1>p2,返回一个正值;如果p1<p2,返回一个负值。
strcat(p1, p2) p2附加到p1之后,返回p1
strcpy(p1, p2) p2拷贝给p1,返回p1

尽量使用vector和迭代器,少用数组

5、与旧代码的接口

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

  • 允许使用以空字符结束的字符数组来初始化string对象,或为string对象赋值。
  • 允许使用以空字符结束的字符数组作为string对象加法运算中的一个对象,不能两个运算对象都是 , 在string对象的复合赋值运算中,允许使用以空字符结束的字符数组作为右侧的运算对象。
  • 上述性质反过来只能用string专门提供的一个名为c_str的成员函数来初始化指向字符的指针。string s ="C++" ; const char *str = s.c_str();该函数的返回结果是一个指针,该指针指向的一个以空字符结束的字读数组, 也就是C风格的字符串,而这个数组所存的数据恰好与那个string对象是一样的,结果指针的类型是const char*,从而确保我们不会改变字符数组的类型。

六、多维数组

  • 多维数组的初始化int ia[3][4] = {{0,1,2,3}, ...}
  • 使用范围for语句时,除了最内层的循环外,其他所有循环的控制变量都应该是引用类型。

七、动态数组

  • 使用 newdelete表达和c中mallocfree类似的功能,即在堆(自由存储区)中分配存储空间。
  • 定义: int *pia = new int[10]; 10可以被一个变量替代。
  • 释放: delete [] pia;,注意不要忘记[]