文章目录
前言
由于本文涉及内容比较多,为了尽可能的讲详细些,会把各种例子附上,所以代码样例会比较多,也为了更好的布局,本系列将分开讲解,并且前期为了更加贴合实战需求,暂时只对一些常用的STL容器和基本功能进行讲解。
为了使每篇文章具有一定的独立性,后续的文章大多数会将之前文章涉及的知识点中提取,并且在后续会将相应的例子补上。
本文STL系列知识参考《C++语言程序涉及(第5版)》
STL指南
- 容器的基本功能与分类
- 顺序容器
- 关联容器
向量
向量支持高效的随机分和高效向尾部加入新元素
是一种可以动态分配的数组,具有和动态访问动态数组几乎一样的效率
原理
当数组空间不够时,向量对象会自动new分配一块更大的空间,使用赋值运算符“=”将原来的数据分别复制到新的空间,并将原有空间释放。
但是每插入一个新元素,向量对象都会将“分配新空间——复制元素——释放原空间”都执行一遍,效率会十分低效。因此,向量在每次扩展空间时,都会分配出大于所需的空间,另一方面,将已有元素删除时,多出的空间并不会释放,这样向量对象的实际容量(capacity)通常大于向量的有效元素个数(size)
插入操作的注意事项
效率在插入新元素时,插入位置后面的元素都需要顺序向后移动,因此插入效率并不高,而在尾部插入时效率较高。
如果插入操作(包括使用push_back)引起向量容量的扩展,那么在插入之前所获得的一切迭代器和指向向量元素的指针、引用都会失效(因为空间被重新分配了,元素的内存地址发生了改变)
失效表示结果不确定。
如果插入操作未引起容量扩展,那么只有插入位置之后的迭代器和指针、引用会失效(因为这些元素移动了),而对插入位置之前的元素不会影响。
删除操作的注意事项
删除向量元素时,被删除元素后面的元素都会向前补齐,删除操作的效率和插入一样,位置越前,时间越多
删除操作不会引起容量的改变,因此被删除元素之前的迭代器和指针、引用都能够继续使用,而删除元素之后的迭代器和指针、引用都会失效。
常用函数
1. s.capacity()
返回s的容量
2. s.reserve(n)
若当前容量大于或等于n,什么也不做,否则扩大s的容量,使s的容量不小于n
在准备向向量插入大量数据前,可以通过s.reserve函数确保分配出适量的空间,避免在插入过程中多次重新分配空间,以提供效率
3. shrink_to_fit
可以将多余的空间回收掉,使得s.capacity()==s.size()
该函数同样适用于string和deque
构造函数
除了使用默认构造函数外,还可以使用给定的元素构造,或者使用已有迭代器的区间所表示的序列来构造
1. S s(n,t);
构造一个由n个t元素构成的容器实例s
int main() {
vector<int>vec1(4,1);
copy(vec1.begin(),vec1.end(),ostream_iterator<int>(cout," "));
}
1 1 1 1
2. S s(n);
构造含有n个大小空间的实例s,每个元素都为默认构造函数T()构造的对象
int main() {
vector<int>vec1(4);
copy(vec1.begin(),vec1.end(),ostream_iterator<int>(cout," "));
}
0 0 0 0
3. s(q1,q2);
将[q1,q2)区间内的数据作为s的元素构造s
int main() {
int a[]={1,2,3,4};
vector<int>vec1(a,a+4);
copy(vec1.begin(),vec1.end(),ostream_iterator<int>(cout," "));
}
1 2 3 4
int main() {
list<int>l(4,2);
vector<int>vec1(l.begin(),l.end());
copy(vec1.begin(),vec1.end(),ostream_iterator<int>(cout," "));
}
2 2 2 2
请注意:下面这串代码就是上一篇博客所说的,这种初始化方法是不对的,它会报错
vector<int>vec1(l.begin(),l.begin()+2);
赋值函数
使用赋值函数时,将会把原有元素全都覆盖
1. s.assign(n,t)
赋值后的s由n个t元素组成,相当于把s用n个t初始化
int main() {
list<int>l(4,2);
vector<int>vec1(l.begin(),l.end());
copy(vec1.begin(),vec1.end(),ostream_iterator<int>(cout," "));
cout<<endl;
vec1.assign(3,1);
copy(vec1.begin(),vec1.end(),ostream_iterator<int>(cout," "));
cout<<endl;
}
2 2 2 2
1 1 1
int main() {
list<int>l(4,2);
vector<int>vec1(l.begin(),l.end());
copy(vec1.begin(),vec1.end(),ostream_iterator<int>(cout," "));
cout<<endl;
vec1.assign(5,1);
copy(vec1.begin(),vec1.end(),ostream_iterator<int>(cout," "));
cout<<endl;
}
2 2 2 2
1 1 1 1 1
s.assign(n)
赋值后的s由n个默认构造函数T()构造的元素组成
这个赋值方法在验证时报错了,所以这里不推荐使用这个
2. s.assign(q1,q2)
赋值后的s的元素为[q1,q2)区间内的元素
int main() {
list<int>l(4,2);
int a[]={1,2,3,4,5};
deque<int>vec1(l.begin(),l.end());
copy(vec1.begin(),vec1.end(),ostream_iterator<int>(cout," "));
cout<<endl;
vec1.assign(a,a+5);
copy(vec1.begin(),vec1.end(),ostream_iterator<int>(cout," "));
cout<<endl;
}
2 2 2 2
1 2 3 4 5
int main() {
list<int>l(4,2);
vector<int>vec={1,2,3,4,5};
deque<int>vec1(l.begin(),l.end());
copy(vec1.begin(),vec1.end(),ostream_iterator<int>(cout," "));
cout<<endl;
vec1.assign(vec.begin(),vec.begin()+5);//此处assign可以使用vec.begin()+n,而在初始化时不可以
copy(vec1.begin(),vec1.end(),ostream_iterator<int>(cout," "));
cout<<endl;
}
2 2 2 2
1 2 3 4 5
元素插入
可以一次插入一个或多个指定元素,也可以将一个迭代器区间所表示的序列插入,插入时需要一个指向s的迭代器表示插入位置。
1. s.insert(p1,t)
在p1位置前插入元素t,返回一个指向新元素的迭代器
int main() {
vector<int>vec1={1,2,3,4,5};
copy(vec1.begin(),vec1.end(),ostream_iterator<int>(cout," "));
cout<<endl;
cout<<*(vec1.insert(vec1.begin()+2,10))<<endl;//返回指向10的迭代器
copy(vec1.begin(),vec1.end(),ostream_iterator<int>(cout," "));
cout<<endl;
}
1 2 3 4 5
10
1 2 10 3 4 5
2. s.insert(p1,n,t)
在p1位置前插入n个元素t,无返回值,书上说无返回值,但实际上是有返回值的,且返回指向第一个新元素的迭代器。
int main() {
vector<int>vec1={1,2,3,4,5};
copy(vec1.begin(),vec1.end(),ostream_iterator<int>(cout," "));
cout<<endl;
vector<int>::iterator it=vec1.insert(vec1.begin()+2,2,10);
cout<<*(it)<<endl;
cout<<*(it+1)<<endl;
cout<<*(it+2)<<endl;
copy(vec1.begin(),vec1.end(),ostream_iterator<int>(cout," "));
cout<<endl;
}
1 2 3 4 5
1 2 10 10 3 4 5
3. s.insert(p1,q1,q2)
在p1位置前插入[q1,q2)区间的元素
4. s.emplace(p1,args)
将参数args传递给T的构造函数构造新元素t,插在p1位置前,返回指向新元素的迭代器
元素删除
1. s1.erase(p1)
删除s1中p1指向的元素,返回被删除的下一个元素的迭代器
2. s1.erase(p1,p2)
删除s1中[p1,p2)区间的元素,返回p2指向的元素的迭代器
改变容器大小
1. s.resize(n)
将容器s大小改为n,若原容器s大小大于n,则末尾多余元素被删除,否则在容器末尾用T()填充
首尾元素的直接访问
1. s.front()
获得首元素的引用
2. s.back()
获得尾元素的引用(不包括forward_list)
在容器尾部插入、删除元素
1. s.push_back(t)
向尾部插入元素t
2. s.emplace_back(args)
将args传递给T的构造函数构造新元素t,向尾部插入新元素
3. s.pop_back()
将容器尾部的元素删除
在容器头部插入、删除元素
1. s.push_front(t)
向头部插入于是元素t
2. s.emplace_front(args)
将参数args传递给T的构造函数构造新元素t,向头部插入新元素
3. s.pop_front()
删除容器头部的元素t
容器列表初始化
c++11支持顺序容器列表初始化,可以直接用列表的元素实例来创建一个新的顺序容器对象,并且隐式地将列表元素个数指定为新建容器大小(定长数组array需指定大小)
如:
list<int>numberSeq={1,4,7,9};//包含4个元素的列表容器对象
vector<string>strs={"hello","world"};//包含2个string元素的向量容器对象
多维向量
vector<int> tr[N];//存储N个一维向量的数组tr
vector<vector<int> >tr;
这两种写法是一样的,都表示二维向量。不过第一种限制了容量大小。
而第一种写法与我们之前见到固定容量的写法有点像,但不一样。
vector<int>tr(n);//固定容量为n的一维向量tr
存储pair类型的向量
对于简单的由两个元素组成的结构,我们可以使用pair来存储。
vector<pair<int,int> >tr;
复杂的则需要使用结构体来定义。