C++9.3容器定义的类型 size_type 、容器执行的操作 insert

简介

先看 9.5节 三种顺序容器的区别

  • 我们已经使用过三种容器定义的类型:size_type、iterator、const_iterator.所有容器都定义这三种类型,还定义了以下的类型。

  • -

===================================================================================================

每种顺序容器都提供了一组有用的类型定义以下操作:

  • 在容器中添加元素;

  • 在容器中删除元素;

  • 设置容器的大小;

  • (如果有的话)获取容器内的第一个和最后一个元素。

(容器能进行哪些操作是要看是哪种容器类型——9.5节)

一、9.3.1 容器定义的类型别名

我们已经使用过三种容器定义的类型:size_type、iterator、const_iterator.所有容器都定义这三种类型,还定义了以下的类型。

C++9.3容器定义的类型 size_type 、容器执行的操作 insert_第1张图片

  • 表中最后三种类型使程序员无须直接知道容器元素的真正类型,就能使用它。需要使用元素类型时,只要用value_type 即可。

  • 如果要引用该类型,则通过reference和const_reference 类型实现。(16章)

//使用容器定义类型的表达式看上去非常复杂

list<string>::iterator iter;
vector<int>::difference_type cnt;

二、9.3.2begin 和 end 成员—-c.begin()和c.end()

  • 上述每个操作都有不同版本:一个是const成员,另一个是非const成员。这些操作返回什么类型取决于容器是否为const;

  • 如果容器不是const,则这些操作返回iterator或reverse_iterator类型

  • 如果容器是const,则返回类型要加上const_前缀,也就是const_iterator和const_reverse_iterator 类型。

三、9.3.3 在顺序容器中添加元素—–使用list 和queue 容器

//在容器中添加元素
string text_word;
while(cin>>text_word)
     container.push_back(text_word);//调用push_back 函数会在容器container尾部创建一个新元素,并使容器的长度加1.新元素的值为text_word 对象的副本,而container的类型则可能是list、vector、deque。

C++9.3容器定义的类型 size_type 、容器执行的操作 insert_第2张图片

除了push_back 运算,list和deque容器还提供了类似的操作:push_front 。这个操作实现在容器首部插入新元素的功能。

为什么vector容器不支持这个操作,因为vector容器元素是按顺序存储的,读取容器,但是添加和删除不方便( 9.5节)

而deque容器针对在首和尾部进行插入和删除操作会比较方便,在其他位置插入和删除操作使用list比较方便( 9.5节)

list<int> ilist;
for(size_t ix=0;ix!=4;ix++)
   ilist.push_back(ix);//使用push_back操作在容器ilist尾部依次添加元素 0、1/2/3

for(size_t iy=0;iy!=4;iy++)
    ilist.push_front(iy);//依次在ilist里面添加元素0,1,2,3
    最后ilist内的元素序列为:3,2,1,0,0,1,2,3,

1、在容器中的指定位置添加元素 –c.insert(p,t)

  • 使用push_back 和push_front 操作可以非常方便地在容器的尾部或首部添加单个元素。

  • 而insert 操作则提供了一组更加通用的插入方法,实现在容器的任意指定位置之前而非之后插入新元素。

  • insert 操作有三个版本,看上表。

vector<string> svec;
list<string> slist;
string spouse("Beth");
slist.insert(slist.begin(),spouse);
svec.insert(svec.begin(),spouse);

2、插入一段元素—c.insert(p,n,t)

svec.insert(svec.end(),10,"Anana");//在svec的尾部插入10个元素,每个元素都是“Anana”
string sarray[4]={"asdsd","sdsds","sdfd","fdfdf"};
//可将数组中所有的或其中一部分元素插入到string类型的list容器中
slist.insert(slist.end(),sarray,sarray+4);
list<string>::iterator slist_iter = slist.begin();
slist.insert(slist_iter,sarray+2,sarray+4);

3、添加元素可能会使迭代器失效——vector或deque容器

  • 在vector容器中添加元素可能会导致整个容器的重新加载,这样的话,该容器涉及的所有迭代器都会失效。即使需要重新加载整个容器,指向新插入元素后面的那个元素的迭代器也会失效。

  • 任何insert或push操作都可能导致迭代器失效,当编写循环将元素插入到vector或deque容器中时,程序必须确保迭代器在每次循环后都得到更新。

//下列程序错在哪里

vector<int>::iterator mid=iv.begin()+iter.size()/2;//事先定义一个迭代器
while(vector<int>::iterator iter!=mid)

  if(iter==some_val)
    iv.insert(iter,2*some_val);
//程序错误!!

//1. 对vector进行插入操作后会导致迭代器失效,而循环中使用了先前保存的迭代器mid(所以我们一般使用迭代器的时候,都是直接在for循环里面使用iter!=ivec.end(),而不是说事先就将ivec.end()赋值给某个变量)

//2. 循环中使用的迭代器iter没有初始化

//3. if语句中的条件应该是比较iter所指向的元素(即*iter)与some_val是否相等

//正确代码如下

vector<int>::iterator iter=iv.begin();
while(iter!=iv.begin()+iv.size()/2){//每次判断的时候都会重载下迭代器,重新计算
    if(*iter==some_val)
     { iter=iv.insert(iter,2*some_val);//使用insert函数在迭代器所指元素的前面插入新元素,返回值为指向新插入元素的迭代器,所以将iter加上2才能指向下一个要处理的原始元素。
      iter+=2;}//指向下一个要处理的元素
     else
      ++iter;//指向下一个要处理的元素
  }

4、避免存储end操作返回的迭代器—-vector和deque容器

  • 在vector和deque容器中添加元素时,因为vector和queue插入元素都要移动元素,可能会导致某些或全部迭代器失效。(list不需要移动元素,因为list非顺序存取)假设所有迭代器失效是最安全的做法,这个建议特别适用于end操作返回的迭代器。在容器的任何位置插入任何元素都会使该迭代器失效。

  • 例如,考虑一个读取容器中每个元素的循环,对读出元素做完处理后,在原始元素后面插入一个新元素。我们希望该循环可以处理每个原始元素,然后使用insert函数插入新元素,并返回指向刚插入元素的迭代器。在每次插入操作完成后,给返回的迭代器自增1,以使循环定位在下一个要处理的原始元素。我们尝试通过存储end操作返回的迭代器来“优化”该循环,将导致灾难性错误:

vector<int>::iterator first=v.begin(),
                      last=v.end();//事先定义了指向end的迭代器,会导致错误!!
while(first!=last){
first=v.insert(first,42);
++first;
//该代码将导致死循环,问题在于这个程序将end操作返回的迭代器值存储在名为last的局部变量中。循环体中实现了元素的添加运算,添加元素会使得存储在last中的迭代器失效。该迭代器既没有指向容器v的元素,也不再指向v的超出末端的下一个位置。
}
  • 为了避免存储end的迭代器,可以在每次做完插入运算后重新计算end迭代器值:
while(first!=v.end()){
first=v.insert(first,42);
++first;
}

你可能感兴趣的:(容器的插入操作,容器定义的类型)