C++既有类模板也有函数模板,vector是类模板。
模板本身不是类或者函数,编译器根据模板创建类或者函数的过程叫实例化。
所以vector生成的类型必须包含vector中元素的类型。
定义和初始化vector对象
默认初始化
vector<int> iVec;默认初始化不含任何元素
拷贝初始化
vector<int> iVec2(iVec);必须保证vector对象类型一致
vector<int> iVec3=iVec;
列表初始化
列表初始化只能使用花括号,不能圆括号。
但花括号在没有匹配的列表初始化情况下可以用到其他初始化
vector<int> iVec4{2,4,6};
初始化的判断依赖于使用的是花括号还是圆括号,整数的含义有可能是容量也可能是元素的值。
vector<int>v1(10);10个元素,每个值为0
vector<int>v2{10};1个元素,值为10
vector对象的下标运算符可用于访问已存在的元素,不能用于添加元素。
迭代器
所有标准库容器都可以使用迭代器,只有少数几种(string,vector)同时支持下标运算符(还有<运算符)。
类似指针类型,迭代器提供对对象间接访问。
和指针不同,获取迭代器不是使用取地址符。
begin成员负责返回指向第一个元素的迭代器,end成员负责返回指向尾元素的下一位置的迭代器。
迭代器为空,begin==end
end返回的迭代器并不实际指示某元素,所以不能进行递增或解引用的操作。
迭代器类型
iterator和const_iterator
const_iterator和常量指针类似,能读取但不能修改所指元素值。vector对象是一个常量,只能使用const_iterator;如果vector对象不是常量,既能使用iterator也可以使用const_iterator。
结合解引用和成员访问操作
箭头运算符(->)把解引用和成员访问结合在一起。
凡是使用了迭代器的循环体,都不能向迭代器所属容器添加元素,否则会导致迭代器失效。
数组
如果不清除元素的确切个数要用vector。
数组声明为a[d],a是数组名,d是维度。维度必须是常量表达式。
字符数组的特殊性
字符数组可以用字符串字面值对数组进行初始化。使用时注意,字符串字面值结尾还有一个空字符,空字符也会被拷贝到字符数组中去。
char a1[]={'c','+','+'};
char a2[]={'c','+','+','\0'};
char a3[]="c++";自动添加空字符
const char a4[5]="hello";错误,实际有6个字符,只有5个空间
不能将数组的内容拷贝给其他数组作为初始值,也不能用数组为其他数组赋值。
复杂的数组声明
int *ptrs[10];ptrs是含有10个整型指针的数组。
int &refs[10];错误,不存在引用的数组。
int (*parray)[10]=&arr;parray指向一个含有10个整数的数组
int (&arrRef)[10]=arr;arrRef引用一个含有10个整数的数组
要想理解数组声明的含义,最好从数组名字开始由内向外顺序阅读。
指针和数组
在大多数表达式中,使用数组类型的对象是使用一个指向数组首元素的指针。
int a[]={2,4,6};
auto a2(a);a2是一个整型指针,指向a的第一个元素
a2=2;错误,不能用int给指针赋值
但如果用decltype,情况不同,转换不会发生
decltype(a) a3={1,3,5};decltype返回类型是3个整型构成的数组。
a3=p;错误,不能用整型指针给数组赋值。
可以通过指针获取数组尾后哪个不存在的元素的地址:
int *e=&arr[10];arr有10个元素,尾元素索引是9,arr[10]这个不存在的元素唯一的用处是提供地址用来初始化e指针。不能对e进行解引用和递增操作。
int a[]={2,4,6};
int *beg=begin(a);指向a首元素的指针
int *last=end(a);指向尾元素下一位置的指针
两指针相减结果类型是ptrdiff_t的标准库类型,为带符号的类型。
下标和指针
只要指针指向的是数组中的元素(或者尾元素的下一位置),都可以执行下标运算。
int a[]={0,2,4,6,8};
int *p=a;p指向a的首元素
i= *(p+2);
int *p1=&a[2];
int j=p1[1];
int k=p1[-2];
标准库类型string和vector也能执行下标运算,但数组和它们比还有所不同,标准库类型限定使用下标为无符号类型,但内置的下标运算可以为负值。
内置的下标运算符所用的索引值不是无符号类型。
C风格字符串
c标准库string函数
strlen ( p );返回p长度,空字符不算在内
strcmp(p1,p2);根据==,>,<分别返回0±
strcat(p1,p2);p2附加到p1,返回p1
strcpy(p1,p2);p2拷贝给p1,返回p1
标准库string对象可以用s1<s2进行比较
但C风格字符串不行,比较的将是指针而不是字符串本身。
对大多数应用来说,使用标准库string比C风格字符串更安全高效。
不能用string对象直接初始化指向字符的指针,string提供了c_str的成员函数:
string s(“hello”);
char * str=s;错误,不能用string初始化char *
const char *str=s.c_str();正确,返回值是C风格的字符串,是一个指针,指向空字符结束的字符数组。
使用数组初始化vector对象
不允许使用vector对象初始化数组,但可以用数组初始化vector对象,需要指明拷贝区域的首元素地址和尾后地址。
C++程序应当尽量使用vector和迭代器,避免使用内置数组和指针;应该尽量使用string,避免使用C风格的基于数组的字符串。
多维数组
严格说没有多维数组,多维数组就是数组的数组。
二维数组第一个维度称作行,第二个称作列。
多维数组的下标引用
如果表达式含有的下标运算符数量和数组维数一样多,表达式结果为给定类型的元素,如果表达式含有的下标运算符数量比数组维度小,表达式结果为给定索引处的一个内层数组。
int(&row)[4]=a[1];把row绑定到a的第二个四元素数组上
使用范围for语句处理多维数组
要使用范围for语句处理多维数组,除了最内层循环外,其他所有循环控制变量都应该是引用类型。
指针和多维数组
由多维数组名转换的来的指针实际上指向第一个内层数组的指针:
int a[2][4];
int (*p)[4]=a;p指向含有四个整数的数组
p=&a[1];p指向a的尾元素
for(auto p=a;p!=a+2;++p)
{
for(auto q=*p;q!=*p+4;++q)
cout<<*q<<endl;
}