一.迭代器失效
1.定义
指原迭代器在扩容/缩容/修改后指向无效元素或无效地址处
erase的迭代器失效
2.原因:
1.有的编译器实现erase会缩容拷贝
2.删除最后一个后,其指向无效元素
VS中不允许再次使用erase完的迭代器,为了让编写的代码移植性强,其通过实现vector迭代器类型为复合一个标识与其他的原生指针。
g++中允许访问失效的迭代器。
3.怎么访问删除的位置
erase中提供了迭代器传入,返回迭代器的方法,用迭代器接收,即为原删除位置的下一个位置。
例:
二.拷贝构造
默认的拷贝构造当类中含有要释放的资源时会析构两次,修改也不能单独修改,对于可以传入自定义类型的vector,需要自己写拷贝构造。
拷贝构造可以复用之前的reserve空间(增加效率,减少频繁开空间消耗),push_back传入的内容。
vector(const vector<T>& v)
{
reserve(v.capacity());
for (auto e : v)
{
push_back(e);
}
}
注意:拷贝构造也算构造,一旦有了,默认构造不在生成,但我们已经给了缺省值,(有自定义构造函数,则无默认构造函数;无自定义复制构造函数,则隐含生成默认复制构造函数。)用
vector() = default;
强制生成默认构造(=default仅可用于写默认成员函数)
三。赋值运算符重载
用“现代写法”传值传参来,再用swap换取拷贝的。(要写vector::swap),std中的swap为浅拷贝且仅有已有类型。
四。不同迭代器初始化
为什么使用迭代器初始化:可以用类的某个区间迭代器初始化
不同类型:用int数组都能初始化vector<char>
用函数模板,支持任意类型的迭代器区间初始化
五。n个value构造
value的缺省值不能是0,有的自定义类型不能从0转化。--》缺省值用匿名对象
(内置类型在C++中也有了构造函数,如:int i(3),int()---->兼容模板,让内置与自定义类型都可以构造)
vector(size_t n,const T&value=T());
六。一个大坑(调用歧义)
vector<char>/vector<int>/...........
vector(10,1)时,因为1与char不匹配,再找,找到迭代器模板函数,两个同型匹配上了,就把它们当作了迭代器.
解决:1.用的时候加个u,表示10是unsigned int
2.给个重载的版本如vector(int n,.....)n改为int,减少问题,n个value的n平时直接传就没问题了
七..花括号初始化
用于多参数的隐式类型转换。一切都可用花括号初始化
1.单参数:A a(1);/A a=1;/A a={1}/A a{1,2}
2.多参数:A a={1,2}不能小括号!
构造+拷贝构造
3.参数个数不固定的,系统支持的initializer_list类型,常量数组的类型{1,2,3,....},
initializer_list:类中由两个指针构成,指向开始与结束,还有size(),begin(),end()返回迭代器(T*)--->使用直接范围for遍历
vector里支持initializer_list的构造
使用:
vector<int> v1={1,2,3,4,5,6};
构造+拷贝构造
进阶使用:
vector<A> v1={A(1),{1},{1,2},1};//隐式类型转化,构造
八。vector存string的大坑
浅拷贝问题:
vector<string> v2;
v2不断push_back,直到insert需要扩容,先开新空间再把memcpy原来的拷贝到新的,两者都指向了同一块空间,再delete[]析构,空间没了,新空间的相当于野指针了。
解决:拷贝新旧空间时像单个拷贝一样用单个的深拷贝tmp[i]=_start[i];
总结:在vector里拷贝也要用单个的深拷贝完成整体深拷贝
list与vector的部分区别
1.string,vector是随机迭代器,list的不是,只支持++,--,不支持+,-,--->代价大
2.不支持[]-->效率低,主流访问方式是迭代器。(_start++/范围for)
3.std里的sort要用随机迭代器,不能排list--->三数取中要支持-
4.多了方法:如逆置,去重(排好序了才能去重(相同值挨着也行)),值删除,list里的sort(归并排序)(效率贼低,不如转为vector,再排,效率能提升3倍)
底层是带头双向循环链表