STL实践与分析
--顺序容器的操作(上)
引:
每种顺序容器都提供了一组有用的类型定义以及以下操作:
1)在容器内添加元素;
2)在容器中删除元素;
3)设置容器的大小;
4)(如果有的话)获取容器内的第一个和最后一个元素。
正文:
一、容器定义的类型别名
所有容器都提供的类型别名 |
size_type |
无符号整型,足以存储容器类型的最大可能容器长度 |
iterator |
容器的迭代器类型 |
const_iterator |
容器的只读迭代器类型 |
reverse_iterator |
按逆序寻址元素的迭代器类型 |
const_reverse_iterator |
元素的只读逆序迭代器 |
difference_type |
足够存储两个迭代器差值的有符号整型,可为负数 |
value_type |
元素类型 |
reference |
元素的左值类型,是value_type&的同义词 |
const_reference |
元素的常量左值类型,等效于constvalue_type& |
逆序迭代器从后向前遍历容器,并反转了某些相关的迭代器操作,例如,在逆序迭代器上做++运 算将指向容器中的前一个元素。
表中最后三种类型使程序员无须直接知道容器元素的真正类型,就能使用它。需要使用元素类型时,只要用value_type即可。如果要引用该类型,则通过reference和const_reference类型实现。在程序员编写自己的泛型程序时,这些元素相关类型的定义非常有用。
使用容器定义类型的表达式看上去非常复杂:
[cpp] view plain copy print ?
- vector<string>::iterator iter;
- vector<int>::difference_type cnt;
vector<string>::iterator iter;
vector<int>::difference_type cnt;
[cpp] view plain copy print ?
-
-
//P273 习题9.16
//int型的vector容器应该使用的索引类型为vector<int>::size_type
[cpp] view plain copy print ?
-
-
-
-
- list<string>::iterator
- list<string>::const_iterator
-
-
-
- list<string>::reverse_iterator
- list<string>::const_reverse_iterator
//习题9.17
/*
*正序
*/
list<string>::iterator
list<string>::const_iterator
/*
*逆序
*/
list<string>::reverse_iterator
list<string>::const_reverse_iterator
二、begin和end成员
容器的begin和end成员 |
c.begin() |
返回一个迭代器,它指向容器c的第一个元素 |
c.end() |
返回一个迭代器,它指向容器c的最后一个元素的下一位置 |
c.rbegin() |
返回一个逆序迭代器,它指向容器c的最后一个元素 而不是下一位置! |
c.rend() |
返回一个逆序迭代器,它指向容器c的第一个元素前面的位置 而不是第一个元素! |
上述每个操作都有两个不同版本:一个是const成员,另一个是非 const成员。这些操作返回什么类型取决于容器是否为const。如果容器不是const,则这些操作返回iterator或reverse_iterator类型。如果容器是 const,则其返回类型要加上const_前缀,也就是const_iterator和const_reverse_iterator类型。
三、在顺序容器中添加元素
在顺序容器中添加元素的操作 |
c.push_back() |
在容器c的尾部添加值为t的元素。返回void类型 |
c.push_front() |
在容器c的前端插入值为t的元素。返回void类型。 只适用于list和deque容器类型。 |
c.insert(p,t) |
在迭代器p所指向的元素前面插入值为t的新元素,返回指向新添加元素的迭代器。 |
c.insert(p,n,t) |
在迭代器p所指向的元素前面插入n个值为t的新元素,返回void |
c.insert(p,b,e) |
在迭代器p所指向的元素前面插入迭代器b和e标记的范围内的元素,返回void。 |
【关键概念】容器元素都是副本
在容器中添加元素时,系统是将元素值复制到容器里。类似地,使用一段元素初始化新容器时,新容器存放的是原始元素的副本。被复制的原始值与新容器中的元素各不相关,此后,容器内元素值发生变化时,被复制的原值不会受到影响,反之亦然。【P274】
[cpp] view plain copy print ?
- list<int> iList;
- for (int i = 0; i != 10; ++i)
- {
- iList.push_back(i);
- iList.push_front(i);
- }
- for (list<int>::const_iterator iter = iList.begin(); iter != iList.end(); ++iter)
- {
- cout << *iter << endl;
- }
list<int> iList;
for (int i = 0; i != 10; ++i)
{
iList.push_back(i);
iList.push_front(i);
}
for (list<int>::const_iterator iter = iList.begin(); iter != iList.end(); ++iter)
{
cout << *iter << endl;
}
1、在容器中的指定位置添加元素
[cpp] view plain copy print ?
- vector<string> strVec;
- list<string> strList;
- string val("Beth");
-
-
- strList.push_front(val);
- strVec.insert(strVec.begin(),val);
vector<string> strVec;
list<string> strList;
string val("Beth");
// strList.insert(strList.begin(),val);
strList.push_front(val);
strVec.insert(strVec.begin(),val); //这个操作的代价是很昂贵的
2、插入一段元素
[cpp] view plain copy print ?
-
- vector<string> strVec;
- strVec.insert(strVec.end(),10,"Ha~");
-
-
- list<string> strList;
- vector<string> strVec;
- string sarray[4] = {"quasi","simba","frollo","scar"};
-
- strList.insert(strList.end(),sarray,sarray+4);
- strVec.insert(strVec.end(),strList.begin(),strList.end());
//1
vector<string> strVec;
strVec.insert(strVec.end(),10,"Ha~");
//2
list<string> strList;
vector<string> strVec;
string sarray[4] = {"quasi","simba","frollo","scar"};
strList.insert(strList.end(),sarray,sarray+4);
strVec.insert(strVec.end(),strList.begin(),strList.end());
3、添加元素可能会使迭代器失效
在vector容器中添加元素可能会导致整个容器的重新加载,这样的话,该容器所涉及到的所有迭代器都会失效!即使不需要重新加载整个容器,指向新插入元素后面的迭代器也会失效!
任何insert或push操作都可能导致迭代器失效,因此在编写循环将元素插入到vector或deque容器中时,程序必须确保迭代器在每次循环后都得到更新。
4、避免存储end操作返回的迭代器
在容器的任何位置插入任何元素都会使end迭代器失效!!!
[cpp] view plain copy print ?
- vector<int>::iterator first = iVec.begin(),
- last = iVec.end();
-
-
- while (first != last)
- {
- first = iVec.insert(++first,42);
- ++ first;
- }
-
-
-
-
-
vector<int>::iterator first = iVec.begin(),
last = iVec.end();
//程序运行期间可能崩溃
while (first != last)
{
first = iVec.insert(++first,42);
++ first;
}
/*
*添加元素会使得存储在last中的迭代器失效,
*该迭代器既没有指向容器iVec的元素,
*也不再指向iVec的超出末端的下一个位置
*/
[cpp] view plain copy print ?
-
- vector<int>::iterator first = iVec.begin();
-
- while (first != iVec.end())
- {
- first = iVec.insert(++first,42);
- ++ first;
- }
//正确的写法
vector<int>::iterator first = iVec.begin();
while (first != iVec.end())
{
first = iVec.insert(++first,42);
++ first;
}
[cpp] view plain copy print ?
-
- int main()
- {
- list<int> iList;
- for (int i = 0; i != 55; ++i)
- {
- iList.push_front(i+1);
- }
-
- deque<int> iDeq1,iDeq2;
-
- for (list<int>::iterator iter = iList.begin(); iter != iList.end(); ++iter)
- {
- if (*iter % 2)
- {
- iDeq1.push_front(*iter);
- }
- else
- {
- iDeq2.push_front(*iter);
- }
- }
-
- cout << "iDeq1:" << endl;
- for (deque<int>::iterator iter = iDeq1.begin(); iter != iDeq1.end(); ++iter)
- {
- cout << *iter << '\t';
- }
- cout << endl << "iDeq2:" << endl;
- for (deque<int>::iterator iter = iDeq2.begin(); iter != iDeq2.end(); ++iter)
- {
- cout << *iter << '\t';
- }
- cout << endl;
- }
//P276 习题9.18
int main()
{
list<int> iList;
for (int i = 0; i != 55; ++i)
{
iList.push_front(i+1);
}
deque<int> iDeq1,iDeq2;
for (list<int>::iterator iter = iList.begin(); iter != iList.end(); ++iter)
{
if (*iter % 2)
{
iDeq1.push_front(*iter);
}
else
{
iDeq2.push_front(*iter);
}
}
cout << "iDeq1:" << endl;
for (deque<int>::iterator iter = iDeq1.begin(); iter != iDeq1.end(); ++iter)
{
cout << *iter << '\t';
}
cout << endl << "iDeq2:" << endl;
for (deque<int>::iterator iter = iDeq2.begin(); iter != iDeq2.end(); ++iter)
{
cout << *iter << '\t';
}
cout << endl;
}
四、关系操作符
所有的容器类型都支持用关系操作符来实现两个容器的比较。比较的容器必须具有相同的容器类型,而且其元素类型也必须相同。例如,vector<int> 容器只能与vector<int>容器比较,而不能与list<int>或 vector<double>容器比较。
容器的比较是基于容器内元素的比较。容器的比较使用了元素类型定义的同一个关系操作符:两个容器做!=比较使用了其元素类型定义的!=操作符。如果容器的元素类型不支持某种操作符,则该容器就不能做这种比较运算。
下面的操作类似于string类型的关系运算:
•如果两个容器具有相同的长度而且所有元素都相等,那么这两个容器就相等;否则,它们就不相等。
•如果两个容器的长度不相同,但较短的容器中所有元素都等于较长容器中对应的元素,则称较短的容器小于另一个容器。
•如果两个容器都不是对方的初始子序列,则它们的比较结果取决于所比较的第一个不相等的元素。
[cpp] view plain copy print ?
- vector<Sales_item> storeA;
- vector<Sales_item> storeB;
- if (storeA < storeB)
vector<Sales_item> storeA;
vector<Sales_item> storeB;
if (storeA < storeB) // error
[cpp] view plain copy print ?
-
- int compVecList(const vector<int> &iVec,const list<int> &iList)
- {
- vector<int> tmp(iList.begin(),iList.end());
-
- if (iVec > tmp)
- return 1;
- else if (iVec < tmp)
- return -1;
- return 0;
- }
-
- int main()
- {
- vector<int> iVec;
- list<int> iList;
- for (int i = 0; i != 10; ++i)
- {
- iVec.push_back(i);
- iList.push_back(i);
- }
- iList.push_back(11);
-
- cout << compVecList(iVec,iList) << endl;
- }
//P278 习题9.20
int compVecList(const vector<int> &iVec,const list<int> &iList)
{
vector<int> tmp(iList.begin(),iList.end());
if (iVec > tmp)
return 1;
else if (iVec < tmp)
return -1;
return 0;
}
int main()
{
vector<int> iVec;
list<int> iList;
for (int i = 0; i != 10; ++i)
{
iVec.push_back(i);
iList.push_back(i);
}
iList.push_back(11);
cout << compVecList(iVec,iList) << endl;
}
五、容器大小的操作
所有容器都提供的大小操作 |
c.size() |
返回容器c中的元素个数。返回类型为c::size_type |
c.max_size() |
返回容器c可容纳的最多元素个数,返回类型为c::size_type |
c.empty() |
返回标记容器大小是否为0的布尔值 |
c.resize(n) |
调整容器c的长度大小,使其能容纳n个元素,如果n<c.size(), 则删除多出来的元素;否则,添加采用值初始化的新元素 |
c.resize(n,t) |
调整容器c的长度大小,使其能容纳n个元素。所有新添加的元素值都为t |
[cpp] view plain copy print ?
- void printList(const list<int> &iList)
- {
- for (list<int>::const_iterator iter = iList.begin(); iter != iList.end(); ++iter)
- {
- cout << *iter << '\t';
- }
- cout << endl;
- }
- int main()
- {
- list<int> iList(10,42);
- printList(iList);
- iList.resize(15);
- printList(iList);
- iList.resize(25,-1);
- printList(iList);
- iList.resize(5);
- printList(iList);
- }
void printList(const list<int> &iList)
{
for (list<int>::const_iterator iter = iList.begin(); iter != iList.end(); ++iter)
{
cout << *iter << '\t';
}
cout << endl;
}
int main()
{
list<int> iList(10,42);
printList(iList);
iList.resize(15);
printList(iList);
iList.resize(25,-1);
printList(iList);
iList.resize(5);
printList(iList);
}
使用resize操作可能会使迭代器失效,在vector或deque容器上做的resize操作有可能会使得所有的迭代器都失效!
对于所有的容器类型,如果resize操作压缩了容器,则指向已删除的元素的迭代器会失效!