《C++ Primer》第三章 字符串、向量和数组
3.1 命名空间的using声明
using namespace::name;
3.2 标准库类型 string
string: 表示可变长的字符序列, 头文件必须包含#include。
标准库string的输入运算符自动忽略字符串开头的空白(包括空格符、换行符、制表符等等),从第一个真正的字符开始读起,直到遇见下一处空白为止。
getline则从给定的输入流中读取数据,直到遇到换行符位置,此时换行符也被读取进来,但是并不存储在最后的字符串中。
初始化string对象方式
- string s1:默认初始化s1为一个空串。
- string s2(s1):直接初始化,s2是s1的副本。
- string s2=s1::拷贝初始化,等价于s2(s1)。
- string s3(“value”):直接初始化,s3是"value"的副本。
- string s3=“value”:拷贝初始化,等价于s3(“value”)。
- string s4(n,‘c’):直接初始化,把s4初始化为由连续n个字符c组成的串。
string操作
-
os<<s:将s写到输出流os当中,返回os
-
is>>s:从is读字符串赋给s,字符串以空白分隔,返回is
-
getline(is,s):从is中读取一行赋给s,返回is
-
s1+s2: 返回s1和s2连接后的结果,要确保+两侧的运算对象至少有一个是string。字面值字符串与string是不同的类型!!!
-
s[n]:返回s中第n个字符的引用,位置n从0开始计算
-
s.empty():s为空返回true,否则返回false
-
s.size():返回s中字符的个数
-
s1 = s2:用s2的副本代替s1中原来的字符
-
s1==s2: 如果s1和s2中所含字符完全一样,则相等
-
<,<=,>,>=:按字典序比较
比较示例:
string str = "Hello";
string phrase = "Hello World";
string slang = "Hiya";
按字典序比较: str<phrase;slang>str; slang>phrase
cctype头文件中的函数
- isalnum©:当c是字母或数字时为真。
- isalpha©:当c是字母时为真。
- iscntrl©: 当c是控制字符时为真。
- isdigit©:当c是数字时为真。
- isgraph©:当c不是空格但可打印时为真。
- islower©:当c为小写字母时为真。
- isprint©:当c是可打印字符时为真(即c是空格或c是可视形式)。
- ispunct©:当c是标点符号时为真。
- isspace©:当c是空白时为真。
- isupper©:当c是大写字母时为真。
- isxdigit©:当c是十六进制数字时为真。
- tolower©: 转换为小写字母。
- toupper©:转换为大写字母。
基于范围的for语句:
for (declaration: expression)
statement
declaration中加上&代表会改变其中的值
string s("Hello World!!!");
for (auto &c : s) //对于s中的每个字符(c是引用)
c = toupper(c); //c是一个引用,因此赋值语句将改变s中字符的值
cout << s << endl;
3.3 标准库类型 vector
vector: 存放的是某种给定类型对象的可变长序列.
定义和初始化vector对象:
- vector v1 v1是一个空vector,潜在的元素是类型的,执行默认初始化
- vector v2(v1) v2中包含v1所有元素的副本
- vector v2=v1 等价于v2(v1)
- vector v3(n,val) v3包含了n个重复的元素,每个元素值都是val
- vector v4(n) v4包含了n个重复地执行了值初始化的对象, 如果是int类型,则默认初始化为0
- vector v5{a,b,c…} v5包含了初始值个数的元素,每个元素被赋予相应的初始值
- vector v5={a,b,c…} 等价于v5{a,b,c…}
使用push_back()添加元素
其余vector操作:
- v.empty() 如果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 当其元素相同时返回true
- <,<=,>,>= 按字典序比较
注意:通过下标访问不存在的元素的行为会导致缓冲区溢出(buffer overflow)。
3.4 迭代器介绍
迭代器:
- 迭代器本身
- 容器定义的迭代器类型
- 某个迭代器对象
auto b = v.begin(), e = v.end();
//b表示v的第一个元素,e表示v尾元素的下一位置
vector<int>::iterator it; //it能读写vector<int>的元素
string::iterator it2; //it2能读写string对象中的字符
vector<int>::const_iterator it3; //it3只能读元素,不能写元素
string::const_iterator it4; //it4只能读字符,不能写字符
begin和end返回的具体类型由对象是否是常量决定,如果是常量,则返回const_iterator;如果不是常量,返回iterator。为了便于得到const_iterator类型的返回值,C++11新标准引入了两个新函数,分别是cbegin和cend。
vector<int> v;
const vector<int> cv;
auto it1 = v.begin(); //it1的类型是vector<int>::iterator
auto it2 = cv.begin(); //it2的类型是vector<int>::const_iterator
auto it3 = v.cbegin(); //it3的类型是vector<int>::const_iterator
标准容器迭代器的运算符:
- *iter :返回迭代器iter所指元素的引用
- iter->mem:解引用iter并获取该元素的名为mem的成员吗,等价于(*iter).mem
- ++iter:令iter指示容器中的下一个元素
- –iter:令iter指示容器中的上一个元素
- iter1 == iter2 判断两个容器是否相等
- iter1!=iter2:同上
将string对象中第一个单词改为大写模式:
//依次处理s的字符直到处理完全部字符或者遇到空白
for (auto it = s.begin();it != s.end() && !isspace(*it); ++it)
*it = toupper(*it);
//依次输出text的每一行直至遇到的第一个空白行为止
for (auto it = text.cbegin(); it != text.cend() && !it->empty(); ++it)
cout << *it << endl;
vector和string迭代器支持的运算
- iter + n:新位置与原来相比向前移动了n个元素
- iter - n:新位置与原来相比向后移动了n个元素
- iter1 += n:将iter1加n的结果赋给iter1
- iter1 -= n:将iter1减n的结果赋给iter1
- iter1 - iter2: 将运算符右侧的迭代器向前移动差值个元素后得到左侧的迭代器
- <,<=,>,>= 迭代器的关系运算符,如果某迭代器指向的容器位置在另一个迭代器所指位置之前,则说前者小于后者。
使用迭代器完成二分搜索:
auto beg = text.begin(), end = text.end();
auto mid = text.begin() + (end-beg)/2;
//当还有元素尚未检查并且我们还没有找到sought时执行循环
while (mid != end && *mid != sought){
if(sought < *mid)
end = mid;
else
beg = mid + 1;
mid = beg +(end - beg)/2;
}
3.5 数组
定义数组时必须指定数组的类型,不允许用auto关键字由初始值的列表推断类型。和vector一样,数组的元素应为对象,因此不存在引用的数组。
不能将数组的内容拷贝给其他数组作为其初始值,也不能用数组为其他数组赋值。示例如下:
int a[]={0,1,2};
int a2[]=a; //错误,不能用一个数组初始化另一个数组
a2 = a; //不能把一个数组直接赋给另一个数组
int *ptrs[10]; //ptrs是含有10个整型指针的数组
int &refs[10]=/* ? */ //错误,不存在引用的数组
int (*Parray)[10] = &arr; //Parray指向一个含有10个整数的数组
int (&arrRef)[10] = arr; //arrRef引用一个含有10个整数的数组
int *(&array)[10] = ptrs; //arry是数组的引用,该数组有10个指针
指针和数组:对数组使用取地址符就能得到指向该元素的指针
指针本身的值是一个内存地址值,表示指针所指对象在内存中的存储地址;指针所指的对象可以通过解引用指针访问;因为指针也是一个对象,所以指针也存储在内存的某个位置,它有自己的地址。
string nums[]={"one","two","three"};
string *p = &nums[0]; //p指向nums的第一个元素
string *p2 = nums; //等价于p2 = &nums[0]
int ia[] = {0,1,2,3,4,5,6,7,8,9};
auto ia2(ia); //ia2是一个整型指针,指向ia的第一个元素
ia2 = 42 //错误:ia2是一个指针,不能用int值给指针赋值
//编译器执行过程
auto ia2(&ia[0]); //ia2的类型是int*
//ia3是一个含有10个整数的数组
decltype(ia) ia3 = {0,1,2,3,4,5,6,7,8,9};
ia3 = p; //错误:不能用整型指针给数组赋值
ia3[4] = i; //正确:把i的值赋给ia3的一个元素
C++ 11 新标准引入了两个名为begin和end的函数,示例如下:
int ia[]={0,1,2,3,4,5,6,7,8,9};
int *beg = begin(ia); //指向ia首元素的指针
int *last = end(ia); //指向arr尾元素的下一位置的指针
c风格字符串的函数: 头文件#include
strlen(p) //返回p的长度,空字符不见算在内
strcmp(p1,p2) //p1==p2,返回0;p1>p2,返回一个正值;否则返回一个负值
strcat(p1,p2) //将p2附加到p1之后
strcpy(p1,p2) //p2拷贝给p1,返回p1
c_str()函数的返回值是一个C风格的字符串,函数的返回结果是一个指针,该指针指向一个以空字符结束的字符数组,而这个数组所存的数据恰好和那个string对象的一样。结果指针的类型是const char*,从而确保不会改变字符数组的内容。
char *str = s; //错误,不能用string对象初始化char*
const char *str = s.c_str(); //正确
使用数组初始化vector元素
int arr[]={0,1,2,3,4,5};
vector<int> ivec(begin(arr),end(arr));
vector<int> vec(arr+1,arr+4); //拷贝三个元素
3.6多维数组
初始化:
int a[3][4]={
{0,1,2,3},
{4,5,6,7},
{8,9,10,11}
};
int a[3][4]={0,1,2,3,4,5,6,7,8,9,10,11};
//显示初始化每行元素
int a[3][4]={{0},{4},{8}};
size_t cnt=0;
for (auto &row: a) //对于外层数组的每一个元素
for (auto &col:row){ //对于内层数组的每一个元素
col = cnt; ///将下一个值赋给该元素
++cnt;
}
int ia[3][4];
int (*p)[4] = ia; //p指向含有4个整数的数组
p = &ia[2]; //p指向ia的尾元素
int *ip[4]; //整型指针的数组
int (*ip)[4]; //指向含有4个整数的数组
//p指向ia的第一个数组
for(auto p = begin(ia);p!=end(ia); ++p){
// q指向内层数组的首元素
for(autro q=begin(*p);q!=end(*p);++q)
cout<<*q<<‘ ’;
cout<<endl;
}
using int_array = int[4];
typedef int int_array[4];
for(int_array *p=ia;p!=ia+3;++p){
for(int *q=*p;q!=*p+4;++q)
cout<<*q<<' ';
cout<<endl;
}