1. vector容器
1.1 容器特性
- 顺序序列:顺序容器中的元素按照严格的线性顺序排序,可以通过元素在序列中的位置访问对应的元素。
- 动态数组:支持对序列中的任意元素进行快速直接访问,甚至可以通过指针算述进行该操作,操供了在序列末尾相对快速地添加/删除元素的操作。
- 能够感知内存分配:容器使用一个内存分配器对象来动态地处理它的存储需求。
1.2 vector容器的初始化
- 直接初始化:
vector<int> vec{1,2,3};//一维容器
; vector<vector<int>> vec{ {1,2,3},{4,5,6} };//二维容器
- 赋值初始化:
vector<int> vec1(5, 1);//带参数构造vec1;vector<int>vec2(vec1);//拷贝构造
- 迭代器初始化:在这种迭代器范围拷贝时,不要求容器类型相同,而且新容器和原容器的元素类型也可以不同,只要能够将要拷贝的元素转换成要初始化的容器元素即可。例如:
int a[5] = { 1,2,3,4,5 };vector<int> c(a, a + 5);//数组迭代器初始化
;vector<int> c2(c1.begin(),c1.begin()+5);//vector容器迭代器初始化;
- 不带参数的构造函数:
vector<int> vec;//初始化一个空的vector,元素个数size()为0
- 带参数的构造函数:
vector<int> vec(10);//初始化size,但每个元素值为默认值
;vector<int> vec(10,1);//初始化size,并且设置初始值
1.3 访问元素
- 数组下标访问:
cout<<vec[i]<<endl;//或者 cout<<vec.at(i)<<endl;
,后者如果index越界,抛出out_of_range,这是两者的区别。
- 使用迭代器:
vector<int>::iterator it;
for(it=vec.begin();it!=vec.end();it++) cout<<*it<<endl;
- 容器的首元素迭代器:
vec.front();
- 容器的尾后迭代器:
vec.back();
- 查找元素是否存在该元素:
find(vec.begin(),vec.end(),value);
在vec.begin()(包括)开始到vec.end()(不包括)中开始查找是否有value的元素,若存在,则返回位置。
- 查找元素值为value在容器的个数:
int num=count(vec.begin(),vec.end(),value);
- 查找符合条件的元素个数:
bool cmp(Student s){
return 90<s.score;
}
int num=count_if(vec.begin(),vec.end(),cmp);
1.4 插入元素
- 在末尾添加元素:
vec.push_back(val);
- 向容器迭代器插入单个元素:
vec.insert(vec.begin()+pos,x);
- 向容器迭代器插入n个相同元素:
vec.insert(vec.begin()+post,n,x);
- 向容器迭代器插入一段其他容器的元素:
vec.insert(vec.begin()+pos,vec1.begin()+beginpos,vec1.begin()+endpos);
1.5 删除元素
- 删除容器最后的元素:
vec.pop_back();
- 删除容器迭代器指向的元素:
vec.erase(vec.begin()+pos);
- 删除容器中[first,last)中元素 :
vec.erase(first,last);
- 清空容器:
vec.clear();
vector.clear()的真正作用是:把size设置成0,但是capacity不变。
- 注意:如果是删除指定位置的元素时,返回值是一个迭代器,指向删除元素的下一个位置;如果是删除某范围内的元素时,返回值也是一个迭代器,指向最后删除元素的下一个位置。
1.6 容器迭代器
- 返回向量头指针,指向第一个元素:
vec.begin()
- 返回向量尾指针,指向向量最后一个元素的下一个位置:
vec.end();
- 反向迭代器,指向最后一个元素:
vec.rbegin();
- 反向迭代器,指向第一个元素之前的位置:
vec.rend();
- 翻转容器:
reverse(vec.begin(),vec.end());
- 容器排序:
sort(vec.begin(),vec.end();
- 删除元素:
vec.erase(remove(vec.begin(),vec.end(),value),vec.end());
,查找得到第一个元素的位置,然后从此位置开始遍历容器,将后面的元素依次前移,跳过与value相同的元素,即所有与value相同的元素均被覆盖,而其他元素都依次向前移动。
- 指向首元素指针:
int *x=vec.data();
链接例题在C++11中,vector 增加了data()的用法,它返回内置vecotr所指的数组内存的第一个元素的指针。 int *p = myvec.data();则p指向myvector的第一个元素指针,而++p;则p指向了myvector的第二个指针,也即p[0]为myvector的第二个元素,则p[2]指向myvector的第四个元素。
- 常量迭代器:
vec.cbegin();vec.cend();
cbegin和cend是不管是对象本身是不是常量,换回值都是const_iterator.
- 常量反向迭代器:
vec.crbegin();vec.crend();
1.7 容器的大小与判断
- 返回值为容器大小:
vec.size();
。
- 重新指定有效元素的个数 :
vec.resize(10);
指定容器有效容器为10个元素,此时vec.size()=10。将vec的现有元素个数调至10个,多则删少则补,其值随机。同时vec.resize(10,2);
是将a的现有元素个数调至10个,多则删少则补,其值为2。
- 返回值为目前容器能存数据的个数:
vec.capacity();
当创建空容器时, 容量(capacity)为 0;当用完时,增加原容量的 1/2 。capacity 一般大于size的原因是为了避免每次增加数据时都要重新分配内存,所以一般会生成一个较大的空间,以便随后的数据插入。注意,每次当容器的内存不够用的时候,需要扩大容器的存储,指的就是capacity。
- 指定容器预留空间,即能存储数据的个数:
vec.reserve(100);
指定容器能够容纳100个单位,此时vec.capacity()=100。不过这一过程是只增不减的,如果n小于当前capacity,否则reserve(n)无效。
- 下面,我们顺便说一下capacity()的大小的问题:
vector<T>
默认构造函数 此时:capacity = 0,使用构造函数vector(n, value)指定capacity 此时:capacity = n。 string (basic_string<char> )
默认构造函数 此时:capacity =15指定了初始字符串的,此时:capacity = 大于字符串长度且等于n*15-1; basic_string<wchar_t>
//不常用,默认构造函数 此时:capacity = 7
指定了初始字符串的 此时:capacity = 大于字符串长度且等于n*8-1;。在上面,我们说到vec.reserve(n)过程是只增不减的,所以无法通过reserve()来收缩空间。因此,当我们需要收缩内存空间的时候,可以使用下面方法:首先新建一个vector/string,然后往新建的vector/string填充原容器的内容,最后将原容器的变量名指向新的容器。代码如下:
vector<int> v(10, 5);
v.reserve(20);
vector<int>(v).swap(v);
- 用不完的内存可以请求归还:在C++11新标准中当内存用不完的时候,可以请求系统归还内存。最后,使得 vec.capacity()==vec.size()。语法如下:
vec.shrink_to_fit();
这里和上面的收缩空间本质是一样的,只不过这里skrink_to_fit(收缩到合适)是利用系统提供的库函数来实现。
- 判断容器是否为空:
vec.empty();
1.8 其他函数
- assign函数:将区间[first,last)的元素赋值到当前的vector容器中,或者赋n个值为x的元素到vector容器中,这个容器会清除掉vector以前的内容。
vec2.assign(vec1.begin(), vec1.end());
或者vec.assign(n,x);
。还有,该函数允许从一个不同但相容的类型赋值,或者从容器的一个子序列赋值。例如:
list<string> newstyle;
vector<const char*> oldstyle;
newstyle=oldstyle;
newstyle.assign(oldstyle.cbegin(),oldstyle.cend());
list<string> list1(1);
list1.assign(10,"hello");
- swap函数:swap操作交换两个容器内容的操作很快,因为元素本身并没有交换,swap只是交换两个容器的内部数据结构,例如:
vec1.swap(vec2);
。元素不会移动的事实,除了stirng外,指向容器的迭代器、引用和指针在swap操作之后都不会失效。它们仍指向swap操作之前所指向的那些元素。但是在swap操作之后,这些元素已经属于不同的容器。
- copy函数:把vec1中的从vec1.begin()(包括它)到vec1.end()(不包括它)的元素复制到vec2中,从vec2.begin()+1的位置(包括它)开始复制,覆盖掉原有元素copy(vec1.begin(),vec1.end(),vec2.begin()+1);
- 关系运算符:每个容器都支持相等运算符(==或者!=);除了无序关联器外的所有容器都支持关系运算符(>,>=,<,<=),关系运算符左右两边的运算对象必须是相同类型,且必须保存相同类型元素。
1.9 emplace操作
- 新标准引进了三个新成员,
emplace_front,emplace,emplace_back
,这些操作构造而不是拷贝元素,也就是当我们调用一个emplace成员函数时,则是将参数传递给元素类型的构造函数。例如:
c.emplace_back("012",25,12.0);
c.push_back("012",25,12.0);
c.push_back(Sales_data("012",25,12.0));