标准库容器是模板类型,用来保存给定类型的对象。一个容器就是一些特定类型对象的集合。
顺序容器
- 顺序容器我们提供了控制元素存储和访问顺序的能力。这种顺序不依赖于元素的值,而是与元素加入容器时的位置对应。
- 一般来说,每个容器都定义在一个都文件中
- 顺序元素几乎可以保存任意类型的元素
顺序容器类型 | 说明 |
---|---|
vector | 可变大小数组。支持快速随机访问。在尾部之外的位置插入或删除元素可能很慢 |
array | 固定大小数组,支持快速随机访问,不能添加或删除元素 |
string | 与vector相似的容器,但专门用于保存字符。随机访问块。在尾部插入或删除快 |
deque | 双端队列。支持快速随机访问。在头尾位置插入或删除速度很快 |
list | 双向链表。只支持双向顺序访问,在list中任何位置进行插入或删除操作的速度都很快 |
forward_list | 单向链表。只支持单向顺序访问,在链表的任何位置进行插入或删除操作的速度都很快 |
forward_list、array是新C++标准增加的类型
与内置数组相比,array是一种更安全、更容易使用的数组类型。
array对象的大小不是固定的,因此,他支持插入和删除元素以及改变容器大小的操作
容器类型成员
每个容器都定义了多个类型
类型别名 | 说明 |
---|---|
iterator | 容器的迭代器类型成员 |
const_iterator | 可以读取元素,但不能修改元素的迭代器类型 |
size_type | 无符号整数类型,足够保存此种容器类型最大可能容器的大小 |
differrnce_type | 带符号整数类型,足够保存两个迭代器之间的距离 |
value_type | 元素类型 |
reference_type | 元素左值类型,与value_type&含义相同 |
const_reference | 元素的const左值类型,(即const value_type&) |
** 通过类型别名,我们可以在不了解容器中元素类型的情况下使用它 **
为了使用这些类型,我们必须显示的使用其类型名
vector
容器的begin成员和end成员
begin成员生成一个指向容器中第一个元素位置的迭代器
end成员生成指向尾元素之后的位置的迭代器
容器定义和初始化
- 每个容器都定义了一个默认的构造函数。容器的默认的构造函数都会创建一个指定类型的空容器,他们都可以接受指定容器大小和元素初始值的参数
- 由于array是固定大小的数组。定义一个array时,除了制定元素类型外,还要指定容器的大小
- 创建一个容器为另一个容器的拷贝时,两个容器的类型以及元素的类型必须相同
- 当传递迭代器参数来拷贝一个范围时,不要求容器的类型必须相同,且新容器和原容器的元素类型也可以不同,只要能将要拷贝的元素的类型转换为要初始化的容器的元素类型即可
定义&初始化方式 | 说明 |
---|---|
C a | 默认构造函数,如果C是一个array,则a中元素按默认方式初始化,否则a为空 |
C a(b) C a=b |
a初始化为b的拷贝。a和b必须是相同的类型(它们必须是相同的容器类型,且保存的是相同的元素类型),对于array我们还要定义它的大小 |
C a{b,c,d,e,f,…} C a={b,c,d,e,f,…} |
a初始化为初始化列表中元素的拷贝。列表中的元素类型必须与a的元素类型相容。对于array来说,列表元素的数目必须小于或等于array的大小,任何遗漏的元素直接进行值初始化 |
C a(b_iterator,c_iterator) | a初始化为迭代器b_iterator和c_iterator指定范围中元素的拷贝,范围中元素的类型必须与a的元素类型相容 |
C a(n) | a包含n个元素,这些元素进行了值初始化,此构造函数explicit的,string和array不适用 |
C a(n,value) | a包含n个初始化为值value的元素 |
#include<iostream>
#include<vector>
#include<list>
#include<forward_list>
using namespace std;
int main()
{
list<string> svec(10,"shan");
for(auto i:svec)
{
cout<<i<<endl;
}
//vector<string> vec = svec;//#错误,容器类型不匹配
forward_list<string> fvec(svec.begin(),svec.end());//使用迭代器范围进行拷贝初始化
for(auto temp:fvec)
{
cout<<temp<<endl;
}
return 0;
}
标准库array的使用
- 定义一个array时,我们要指定元素的类型,还要指定容器的大小
- 由于大小是array类型的一部分,array不支持不同容器类型的构造函数
- 对array进行列表初始化的时候,初始值的数目必须等于或小于array的大小
- array要求初始值的类型必须要与创建的容器类型相一致
- 虽然我们不能对内置的数组进行拷贝或对象赋值操作,但是array并无此限制
#include<iostream>
#include<array>
using namespace std;
int main()
{
array<int ,10> num_1;//定义了一个保存10个int的数组
for(auto i:num_1)
cout<<i<<" ";
cout<<endl;
array<int ,10> num_2={1,2,3,4,5,6,7,8,9,10};
for(auto i:num_2)
cout<<i<<" ";
cout<<endl;
int d[5]={3,6,9,12,15};
//int b[5]=d; 这是错误的,内置数组不支持拷贝和赋值
array<int ,6> try_1={4,5,6,7,8,9};
array<int ,6> try_2=try_1;
for(auto temp:try_2)
cout<<temp<<" ";
return 0;
}
容器操作
swap和assign
- a.swap(b):用于交换两个容器中的元素,两个容器必须具有相同的类型
- swap(a,b):用于交换两个相同类型的容器中的元素
- a.assign(1_iterator,2_iterator):将a中的元素替换为迭代器1_iterator和2_iterator范围中的元素,迭代器不能指向a中的元素
- a.assign(value_list): 将a中的元素初始化为初始化列表value_list中的元素
- a.assign(n,value):将a中的元素替换为n个值为value的元素
#include<iostream>
#include<vector>
using namespace std;
int main()
{
vector<int> a={1,2,3,4,5,6};
vector<int> b;
b.assign({6,7,8,9,10,11});
cout<<endl;
for(auto temp:b)
cout<<temp<<" ";
cout<<endl;
b.assign(a.begin(),a.end());
for(auto temp:b)
cout<<temp<<" ";
cout<<endl;
/*
* swap操作交换两个相同类型容器的内容
* assing操作用参数所指定的元素(即拷贝)替换左边容器中的所有元素。
* assign允许我们从不同但相容的类型赋值,或者从一个容器的子序列赋值
* 除了string外,指向容器的迭代器、引用和指针在swap操作后都不会失效
*/
vector<int> temp={11,22,33,44,55,66,77,88};
b.swap(temp);
cout<<"temp: ";
cout<<"size-"<<temp.size()<<" ";
for(auto i=temp.begin();i != temp.end();i++)
cout<<*i<<" ";
cout<<endl;
cout<<endl;
cout<<"b: ";
cout<<"size-"<<b.size()<<" ";
for(auto j=b.begin();j != b.end();j++)
cout<<*j<<" ";
return 0;
}
向容器中添加元素(array不支持这些操作)
说明 | |
---|---|
C.push_back(t) C.emplace_back(args) |
在C的尾部创建一个值为t或由args创建的元素。返回void类型 |
C.push_front(t) C.emplace_front(args) |
在C的头部创建一个值为t或由args创建的元素,返回指向新添加的元素的迭代器 |
C.insert(p_iterator,t) C.emplace(p_iterator,args) |
在迭代器p_iterator之前插入一个值为t或由args创建的元素,返回指向新添加的元素的迭代器 |
C.insert(p_iterator,n,t) | 在迭代器p_iterator指向的元素之前插入n个值为t的元素,返回指向新插入的第一个元素的迭代器,若n为0,则返回p_iterator |
C.insert(p_iterator,a_iterator,b_iterator) | 将迭代器a_iterator和b_iterator指定的范围内的元素插入到迭代器p_iterator指向的元素之前,迭代器范围不能指向C中的元素。返回指向第一个新添加的元素的迭代器,若范围为空,则返回p_iterator |
C.insert(p_iterator,li) | 将由花括号括起来的元素值列表li插入待迭代器p_iterator所指的元素之前。返回新添加的第一个元素的迭代器,若列表为空,则返回p_iterator |
- 向一个vector、string或deque中插入元素会使所有指向容器的迭代器、引用和指针失效
- 记住,insert函数将元素插入到迭代器所指定的位置之前
- 当我们用一个对象来初始化容器时,或将一个对象插入到容器中的时,实际上放入到容器中的是对象的值的拷贝,而不是对象本身
- vector、list、deque、string都支持insert成员,forward_list提供了特殊版本的insert成员
#include<iostream>
#include<vector>
#include<list>
using namespace std;
void operated_1()
{
vector<string> ss{"shansan","wocao","yeshan"};
vector<string> v;
for(auto i:ss)
v.push_back(i);
for(auto temp:v)
cout<<temp<<" ";
}
void operated_2()
{
list<int> ls;
for(int num=3;num>=0;num--)
ls.push_front(num);
for(int i=5;i<8;i++)
ls.push_back(i);
for(auto i=ls.begin();i != ls.end(); i++)
cout<<*i<<" ";
}
void operated_3()
{
vector<int> v_1{1,2,3,4,5};
vector<int> v_2{6,7,8,9,10};
v_1.insert(v_1.begin(),66);//在迭代器v_1.begin()所指的元素之前插入一个元素66
for(auto temp: v_1)
cout<<temp<<" ";
cout<<endl;
v_1.insert(v_1.begin(),v_2.begin(),v_2.end());
//在迭代器v_1.begin()所指的元素之前,插入迭代器v_2.begin()到v_2.end()指顶范围内的元素
for(auto temp:v_1)
cout<<temp<<" ";
}
int main()
{
operated_1();
cout<<endl;
operated_2();
cout<<endl;
operated_3();
return 0;
}
emplace操作
当调用一个insert或push成员函数时,我们将元素类型的对象传递给它们,这些对象被拷贝到容器中
当调用一个emplace函数时,则是将参数传递给元素类型的构造函数。emplace成员直接使用这些参数在容器管理的内存空间中直接构造函数
emplace函数在容器中直接构造函数。传递给emplace函数的参数必须与元素类型的构造函数相匹配
#include<iostream>
#include<vector>
using namespace std;
class student{
public:
student(string str,int a):name(str),age(a){}
void print_1()
{
cout<<"name: "<<this->name<<","<<"Age: "<<this->age<<endl;
}
private:
int age;
string name;
};
int main()
{
vector<student> s;//我们将student存到了vector中
s.emplace_back("shansan",18);//使用student对象的构造函数
auto temp=s.begin();
(*temp).print_1();
cout<<endl;
//s.push_back("try",66);//error:没有接受两个参数的push_back版本
s.push_back(student("try",66));//创建一个临时的student对象传递给push_back
for(auto i:s)
i.print_1();
return 0;
}
访问容器中的元素
访问操作 | 说明 |
---|---|
c.back() | 返回c中尾元素的引用。若c为空,函数行为未定义 |
c.front() | 返回c中首元素的引用。若c为空,函数行为未定义 |
c[n] | 返回c中下标为n的元素的引用,n是一个无符号整数。若n>=c.size(),则函数行为未定义 |
c.at(n) | 返回下标为n的元素的引用。如果下标越界,则抛出一个out_of_range异常 |
#include<iostream>
#include<vector>
using namespace std;
int main()
{
/*
* 不要对一个空的容器使用front和back操作
* at和下标操作只适用于string、vector、deque和array
* back不适用于forward_list
*/
vector<int> v{1,6};
int a = v.front();
a=33;
cout<<"v[0]="<<v[0]<<endl;
int &refer = v.front();
refer =33;//改变了v[0]的值
cout<<"v[0]="<<v[0];
return 0;
}
由于访问成员函数的返回值是引用类型,如果是非const的,我们可以使用它来改变元素的值
删除容器中的元素
删除操纵 | 说明 |
---|---|
c.pop_back() | 删除c的尾元素,如果c是空的,则函数行为未定义。函数返回void |
c.pop_front() | 删除c的首元素,如果c是空的,则函数行为未定义。函数返回void |
c.erase(p_iterator) | 删除迭代器p_iterator所指定的元素,返回一个指向被删除的元素之后元素的迭代器,若p_iterator指向尾元素,则返回尾后迭代器,若p_iterator是尾后迭代器,则函数行为未定义 |
c.erase(a_iterator,b_iterator) | 删除迭代器a_iterator和b_iterator所指定范围内的元素,返回一个指向最后一个被删元素之后元素的迭代器,若b_iterator本身就是尾后迭代器,则返回尾后迭代器 |
c.clear() | 删除c中所有的元素 |
删除deque中除首尾位置之外的任何元素都会使迭代器、引用和指针失效。指向vector或string中删除点位置之后的迭代器、引用和指针都会失效
#include<iostream>
#include<vector>
#include<list>
using namespace std;
int main()
{
vector<string> str{"shansan","wocao","555"};
for(auto temp:str)
cout<<temp<<" ";
str.pop_back();
cout<<endl;
for(auto temp:str)
cout<<temp<<" ";
cout<<endl;
str.erase(str.begin(),str.end());
for(auto temp:str)
cout<<temp<<" ";
list<int> lst={0,1,2,3,4,5,6,7,8,9,10};
auto it = lst.begin();
while(it != lst.end())
{
if(*it%2)
it = lst.erase(it);//删除奇数元素,返回指向被删除元素的下一个元素的迭代器
else
it++;
}
for(auto temp:lst)
cout<<temp<<" ";
return 0;
}
改变容器大小
- c.resize(n):调整c的大小为n个元素。若n<c.size(),则多出的元素被丢弃;若必须添加新元素,对新元素进行值初始化
- c.resize(n,t):调整c的大小为n个元素,任何新添加的元素都初始化为值t
#include<iostream>
#include<vector>
#include<deque>
using namespace std;
int main()
{
vector<int> v{22,33,3};
cout<<"v capacity:"<<v.capacity()<<endl;
v.resize(6,1);
cout<<"v size:"<<v.size()<<endl;
cout<<"v capacity:"<<v.capacity()<<endl;
for(auto i:v)
cout<<i<<" ";
/*
- 使用reserve操作通知容器应该为我们预留多少空间
- resize成员函数只改变容器中成员函数的数目,不改变容器的容量
- 容器的size操作指的是它已经保存的元素的数目
- capacity则是在不不分配新的内存空间的前提下知道它最多可以保存多少个元素
*/
v.reserve(100);
cout<<endl<<"v capacity:"<<v.capacity()<<endl;
cout<<"v size:"<<v.size()<<endl;
return 0;
}