- 第三章 字符串、向量和数组
- 3.1 标准库类型string
- 3.1.1 直接初始化和拷贝初始化
- 3.1.2 size_type类型
- 3.1.3 字面值和string对象相加
- 3.2 标准库类型vector
- 3.2.1定义和初始化vector对象
- 3.2.2 向vector对象中添加元素
- 3.3 迭代器
- 3.3.1使用迭代器
- 3.3.2 迭代器运算
- 3.4数组
- 3.4.1数组初始化
- 3.4.2指针和数组
- 3.5 多维数组
- 3.5.1多维数组的初始化
- 3.5.2使用范围for语句处理多维数组
如果使用等号初始化一个变量,实际上执行的是拷贝初始化,编译器将等号右侧初始值拷贝到新创建的对象中去;如果不使用等号,则执行的是直接初始化
string s5 = "hello";//拷贝初始化
string s6("hello");//直接初始化
3.1.2 size_type类型
size函数返回的是一个string::size_type类型,它是一个无符号类型的值,而且能足够存放任何string对象的大小
如果一条表达式中已经有了size()函数就不要再使用int这样可以避免混用unsigned int 和int带来的问题
3.1.3 字面值和string对象相加当把string对象和字符字面值及字符串字面值混在一条语句中使用时,必须确保每个加法运算符的两侧的运算对象至少有一个是string
string s1 = "hello"; string s2 = s1 + ",";//正确:把一个string和字面值相加 string s3 = "hello" + ",";//错误:两个运算对象都不是string3.2 标准库类型vector
note: vector是模板而非类型,由vector生成的类型必须包含vector中元素的类型,例如
vector3.2.1定义和初始化vector对象
vectorv1 //v1是一个空vector,它潜在元素是T类型的,执行默认初始化 vector v2(v1) //v2中包含v1所有元素的副本 vector v2 = v1 //等价于v2(v1) vector v3(n,val) //v3中包含了n个重复元素,每个元素的值都是val vector v4(n) //v4中包含了n个重复地执行了值初始化的对象 vector v5{a,b,c...} //v5中包含了初始值个数的元素,每个元素被赋予相应的初始值 vector v5 = {a,b,c ...} //等价于vector v5{a,b,c...}
列表初始化vector对象时应注意:
如果提供的是初始元素值的列表,则只能把初始值放在花括号里进行列表初始化,而不能放在圆括号里
vectorv1{"a","an","the"}; //列表初始化 vector v1("a","an","the"); //错误
值初始化:只提供vector对象容纳的元素数量,此时库会创建一个值初始化的元素初值
vectorivec(10); //10个元素,每个都初始化为0
列表初始化还是元素数量 PS:()与{}区分
vectorv1(10); //v1中有10个元素,每个的值都是0 vector v2{10}; //v2中有1个元素,该元素的值是10 vector v3(10,1); //v3中有10个元素,每个的值是1 vector v4{10,1}; //v4中有两个元素,10和1 vector v5{"hi"}; //列表初始化 v5中有一个元素 vector v6("hi"); //错误,不能用字符串字面值构建vector对象 vector v7{10}; //v7中有10个默认初始化的元素 vector v8{10,"hi"};//v8中有10个值为"hi"的元素
综上 圆括号表示提供的值是用来构造对象的,花括号则表示我们想列表初始化,但需注意,花括号的值必须与元素类型相同,如v7和v8,提供的值不能作为元素的初始值,编译器会尝试使用默认值初始化vector对象
3.2.2 向vector对象中添加元素vector.push_back(t);//用于向vector的尾部添加一个值为t的元素
应注意,不能用下标形式添加元素
vectorivec;//空vector对象 for(decltype(ivec.size()) ix = 0; ix != 10; ++ix) ivec[ix] = ix; //严重错误:ivec中不包含任何元素 //正确方法应该是使用push_back
只能对确知已经存在的元素执行下标操作
3.3 迭代器 3.3.1使用迭代器获取迭代器不是使用取地址符,有迭代器的类型同时拥有返回迭代器的成员。如begin和end
//a表示v的第一个元素,e表示v尾元素的下以位置 auto a = v.begin, e = v.end;
end成员返回的迭代器常被称作尾后迭代器。特殊情况下如果容器为空,则begin和end返回的是同一个迭代器
将迭代器从一个元素移动到另一个元素:
迭代器使用递增(++)运算符,来从一个元素移动到下一个元素,迭代器的递增是将迭代器“向前移动一个位置”。应注意:end返回的迭代器并不实际指示某个元素,所以不能对其递增或者解引用。
迭代器类型
那些拥有迭代器的标准库类型使用iterator和const_iterator来表示迭代器的类型
iterator的对象可读可写
const_iterator和常量指针差不多,能读取,但不能修改它所指的元素值。
begin 和 end运算符
begin和end返回的具体类型由对象决定,如果对象是常量,begin和end返回const_iterator,如果不是常量,返回iterator;
cbegin和cend
类似于begin和end,不同的是不论对象本身是否是常量,返回的都是const_iterator
某些对vector对象的操作会使迭代器失效
不能在范围for循环中向vector对象添加元素,但凡是使用了迭代器的循环体,都不要向迭代器所属的容器中添加元素
3.3.2 迭代器运算iter + n 迭代器加上一个整数值仍得一个迭代器,迭代器指示的新位置与原来相比向前移动了若干个元素
iter - n 迭代器j减去一个整数值仍得一个迭代器,迭代器指示的新位置与原来相比向后移动了若干个元素
iter - n 迭代器j减去一个整数值仍得一个迭代器,迭代器指示的新位置与原来相比向后移动了若干个元素
iter1+=n 迭代器加法的复合语句,将iter1加n的结果赋给iter1
iter1-=n 迭代器加法的复合语句,将iter1减n的结果赋给iter1
iter1-iter2 两个迭代器相减的结果是它们之间的距离。参与运算的两个迭代器必须指向的是同一个容器中的元素或者尾元素的下一位置。
>,>=,<,<= 迭代器的关系运算符,如果某迭代器指向的容器位置在另一个迭代器所指位置之前,则说前者小于后者。参与运算的两个迭代器必须指向的是同一个容器中的元素或者尾元素的下一位置。
difference_type:两个迭代器相减的结果是它们之间的距离的类型,因为这个距离可正可负,所以是个带符号类型的
3.4数组数组中元素的个数也属于数组类型的一部分,编译的时候应该是已知的;
和内置类型的变量一样,如果在函数内部定义了某种内置类型的数组,那么默认初始化会令数组含有未定义的值
不允许使用auto关键字由初始值的列表推断类型,数组的元素应为对象,因此不存在引用的数组
3.4.1数组初始化字符数组的特殊性
当使用字符串字面值对此类数组初始化时,一定要注意字符串字面值的结尾处还有一个空字符,这个空字符也会像其他字符一样被拷贝到 字符数组中去
char a1[] = {'C','+','+'}; //列表初始化,没有空字符
char a2[] = {'C','+','+',' '}; //列表初始化,含有显式的空字符
char a3[] = "C++"; //自动添加表示字符串结尾的空字符
const char a4[] = "daniel";//错误:没有空间可存放空字符
理解复杂的函数声明
int *ptrs[10]; //ptrs是含有10个整型指针的数组 int &refs[10] = ; //错误:不存在引用的数组 int (*Parray)[10] = &arr; //Parray指向一个含有10个整数的数组 int (&arrRef)[10] = arr;//arrRef引用一个含有10个整数的数组
要想理解数组声明的含义,最好的办法是从数组的名字开始按照由内向外的顺序阅读
3.4.2指针和数组当使用数组作为一个auto变量的初始值时,推断得到的类型时指针而非数组
int ia[] = {0,1,2,3,4,5,6,7,8,9};//ia是一个含有10个整数的数组
auto ia2(ia); //ia2是一个整型指针,指向ia的第一个元素
//编译器实际执行的初始化:
auto ia2(&ia[0]);
当使用decltype关键字时上述转换不会发生
decltype(ia)//返回的类型是由10个整数构成的数组
指针也是迭代器
vector和string的迭代器支持的运算,数组的指针全都支持。
标准库函数begin和end
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); //指向尾元素下一位置的指针
注意:尾后指针不能执行解引用和递增操作
3.5 多维数组 3.5.1多维数组的初始化允许使用花括号括起来的一组值初始化多维数组
如果仅仅想初始化每一行的第一个元素:
//显式地初始化每行的首元素,其他未列出的元素执行默认值初始化
int ia[3][4] = {{0},{4},{8}};
//显式地初始化第1行,其他元素执行值初始化
int ia[3][4] = {0,3,6,9}
3.5.2使用范围for语句处理多维数组
例子如下:
for(const auto &row : ia)
{
for(auto col : row)
{
cout << col << endl;
}
}
这里将外层循环的控制变量声明成了引用类型,这是为了避免数组被自动转成指针,假设不用引用类型:
for(auto row : ia)
for(auto col : row)
程序将无法通过编译,此时row不是引用类型,所以编译器初始化row时,自动将这些数组形式组成的元素转换成数组内首元素的指针,即row类型为int *
要使用范围for语句操作多维数组,除了最内层的循环外,其他所有循环的控制变量都应该是引用类型



