向顺序容器添加元素
除了 array
之外,所有的标准库容器都提供了灵活的内存管理,在运行时可以动态增加或删除元素来改变容器的大小。
使用 push_back
除了 array 和 forward_list之外,每个顺序容器(包含string类型)都支持 push_back。
void pluralize(size_t cnt,string &word)
{
if(cnt > 1)
word.push_back('s'); //等价于 word += 's'
}
使用 push_front
除了 push_back
,list
、forward_list
和 deque
容器还支持 push_front
的类似操作,该操作将元素插入到容器头部。
list<int> ilist;
for(size_t ix=0;ix!=4;++ix)
ilist.push_front(ix);
在容器中的特定位置添加元素
insert
成员提供了更一般的添加功能,它允许在容器中任意位置插入0个或多个元素。
vector
、deque
、list
和 string
都支持 insert
。
forward_list
提供了特殊版本的 insert成员。
insert
函数接受一个迭代器作为其第一个参数,迭代器指出了在容器中什么位置放置新元素。它可以指向容器的任何位置,包括容器尾部之后的下一个位置。
insert
函数将元素插入到迭代器所指定的位置之前。
slit.insert(iter,"hello"); //将hello插入到iter之前的位置
由于某些容器不支持 push_front
操作,但它们对于 insert
操作并无类似的限制,因此可以将元素插入到容器的开始位置,而不必担心容器是否支持 push_front
。
vector<string> svec;
list<string> slist;
// 等价于调用slist.push_front("hello")
slist.insert(slist.begin(),"hello");
//vector 不支持push_front,但是可以在begin()之前加入元素
svec.insert(svec.begin(),"hello");
把元素插入到 vector
、deque
、string
中的任何位置都是合法的,然而,这样做可能很耗时。
插入范围内元素
insert
函数除了接受一个迭代器参数外,还可以接受一个元素数目和一个值,它将指定数量的元素添加到指定位置之前,这些元素都按照给定的值初始化。
svec.insert(svec.end(),10,"Anna");
这行代码将10个元素插入到 svec
的末尾,并将所有的元素都初始化 "Anna"
。
接受一个迭代器或一个初始化列表的insert版本将给定范围中的元素插入到指定的位置之前:
vector<string> v = {"quasi","simba","frollo","scar"};
//将v的最后两个元素添加到slist的开始位置
slist.insert(slist.begin(),v.edn()-2,v.end());
slist.insert(slist.end(),{"these","words","will","go","at","the","end"});
//运行时错误,迭代器表示要拷贝的范围,不能指向与目的位置相同的容器
slist.insert(slist.begin(),slist.begin(),slist.end());
如果传递给 insert
一对迭代器,它们不能指向添加元素的目标容器。
如果范围为空,不插入任何元素,insert
操作会将第一个参数返回。
使用 insert 的返回值
通过使用 insert 的返回值,可以在容器中一个特定位置反复插入元素:
list<string> lst;
auto iter = lst.begin(); //将iter初始化为lst.begin()
while(cin >> word)
iter = lst.insert(iter,word); //等价于调用push_front
使用 emplace 操作
emplace_front
,emplace
,emplace_back
这些操作不是构造而是拷贝元素,这些操作分别对应 push_front
,insert
,push_back
,允许将元素放置在容器头部,一个指定位置之前或容器尾部。
调用 push
或 insert
函数是将元素类型的对象传递给它们,这些对象被拷贝到容器中。
调用 emplace
时是将参数传递给元素类型的构造函数,emplace
成员使用这些参数在容器管理的内存中直接构造元素。
//在c的末尾构造一个 Sales_data 对象
//使用三参数的 Sales_data 构造函数
c.emplace_back("9-999-999-9",25,12,99);
//没有接受三个参数的push_back版本
c.push_back("9-999-999-9",25,12,99);
//创建一个临时的 Sales_data 对象,并将其传递给push_back
c.push_back(Sales_data("9-999-999-9",25,12,99));
emplace 函数的参数根据元素类型而变化,参数必须与元素类型的构造函数相匹配。
c.emplace_back(); //使用Sales_data的默认构造函数
c.emplace(iter,"9-999-999-9"); //使用Sales_data(string)的构造函数
c.emplace_front("9-999-999-9",25,12,99); //使用接受一个ISBN,COUNT,PRICE版本的构造函数
访问元素
包括 array
在内的每个顺序容器都有一个 front
成员,而除 forward_list
之外的所有容器都有一个 back
成员函数,这两个操作分别返回首元素和尾元素的引用。
//在解引用一个迭代器或调用front或back之前需要检查是否有元素
if (!c.empty())
{
// val 和 val2 是c中第一个元素值的拷贝
auto val = *c.begin(), val2 = c.front();
//val3 和 val4 是c中最后一个元素值的拷贝
auto last = c.end();
auto val3 = *(--last); //不能递减forward_list迭代器
auto val4 = c.back(); //forward_list不支持
}
迭代器 end
指向的是容器尾元素之后的元素,为了获取尾元素,必须首先递减此迭代器。
在调用 front
和 back
(解引用begin 或 end 返回的迭代器)之前,要确保 c
非空,如果容器为空,if
操作的行为将是未定义的。
访问成员函数返回的是引用
在容器中访问元素的成员函数即 front、back、下标和at返回的都是引用。
如果容器是一个 const对象,则返回值是const的引用。如果容器不是const的,则返回值是普通的引用,可以用来改变元素的值。
if (!c.empty())
{
c.front() = 42; //将42赋值给c中的第一个元素
auto &v = c.back(); //获取指向最后一个元素的引用
v = 1024; //改变c中的元素
auto v2 = c.back(); //v2不是一个引用,它是c.back() 的一个拷贝
v2 = 0; //未改变c中的元素
}
下标操作和安全的随机访问
提供快速随机访问的容器 string
、vector
、deque
、array
也都提供下标运算符。
使用越界的下标是一种严重的错误,并且编译器并不检查这种错误。
如果希望确保下标是合法的,可以使用 at
成员函数,使用 at
成员函数,如果下标越界,at
会抛出一个 out_of_range
异常。
vector<string> svec;
cout<<svec[0]; //运行时错误,svec中没有元素
cout<<svec.at(0); //抛出一个out_of_range异常
删除元素
非 array
容器也有多种删除方式。
pop_front 和 pop_back 成员函数
pop_front
和 pop_back
成员函数分别删除首元素和尾元素。
与 vector
和 string
不支持 push_front
一样,这些类型也不支持 pop_front
,forward_list
不支持pop_back
。
不能对一个空容器执行弹出操作。
while(!ilist.empty())
{
process(ilist.front()); //对ilist的首元素进行处理
ilist.pop_front(); //完成处理后删除首元素
}
从容器内部删除一个元素
成员函数 erase
从容器中指定位置删除元素:
- 可以删除由一个迭代器指定的单个元素。
- 也可以删除由一对迭代器指定的范围内的所有元素。
两种形式的 erase
都返回指向删除的(最后一个)元素之后位置的迭代器。
list<int> lst = {0,1,2,3,4,5,6,7,8,9};
auto it = lst.begin();
while(it != lst.end())
{
if(*it % 2) //如果元素是奇数
it = lst.erase(it); //删除此元素
else
++it;
}
删除多个元素
接受一对迭代器的 erase 版本允许删除一个范围内的元素:
//删除两个迭代器表示的范围内的元素
//返回指向最后一个被删除元素之后位置的迭代器
elem1 = slist.erase(elem1,elem2); //调用后,elem1 == elem2
如果要删除一个容器中的所有元素:
- 可以使用 密密麻麻扩扩扩军军,,,,,,clear。
- 也可以用 begin 和 end 获取的迭代器作为参数调用erase。
slist.clear();
slist.erase(slist.begin(),slist.end());