向顺序容器添加元素

除了 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_backlistforward_listdeque 容器还支持 push_front 的类似操作,该操作将元素插入到容器头部。

list<int> ilist;
for(size_t ix=0;ix!=4;++ix)
	ilist.push_front(ix);

在容器中的特定位置添加元素

insert 成员提供了更一般的添加功能,它允许在容器中任意位置插入0个或多个元素。

vectordequeliststring 都支持 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");

把元素插入到 vectordequestring 中的任何位置都是合法的,然而,这样做可能很耗时。

插入范围内元素

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_frontemplaceemplace_back 这些操作不是构造而是拷贝元素,这些操作分别对应 push_frontinsertpush_back,允许将元素放置在容器头部,一个指定位置之前或容器尾部。

调用 pushinsert 函数是将元素类型的对象传递给它们,这些对象被拷贝到容器中。

调用 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 指向的是容器尾元素之后的元素,为了获取尾元素,必须首先递减此迭代器。

在调用 frontback (解引用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中的元素
}

下标操作和安全的随机访问

提供快速随机访问的容器 stringvectordequearray 也都提供下标运算符。

使用越界的下标是一种严重的错误,并且编译器并不检查这种错误。

如果希望确保下标是合法的,可以使用 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_frontpop_back 成员函数分别删除首元素和尾元素。

vectorstring 不支持 push_front 一样,这些类型也不支持 pop_frontforward_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());