C++ Vector swap操作前后迭代器为何不失效

http://blog.chinaunix.net/uid-28387257-id-3968233.html

作者:henrystark henrystark@126.com 
Blog: http://henrystark.blog.chinaunix.net/
日期:20131029
本文可以自由拷贝,转载。但转载请保持文档的完整性,注明原作者及原链接。 如有错讹,烦请指出。


swap操作

swap是STL泛型操作的一种。这种操作的时间复杂度极低,用于两个容器内容的呼唤。
例如定义vector vi1和vi2, vi1.swap(vi2),就将vi1和vi2的内容交换了。

迭代器

iterator实际上是一种指针,可以指向容器的任意位置。例如vector::iterator it1 = vi1.begin();
这两个操作本身很简单,但是怪异的一点是swap前后,迭代器不失效,原来指向什么内容,swap后还是指向什么内容。
例如下面这段代码:

#include 
#include 

using namespace std;

int main()
{
    vector v1(10,1);
    vector v2(100,2);
    vector::iterator it1 = v1.begin();
    vector::iterator it2 = v2.begin();
    cout<< &v1[0] << "\t" <<  *it1 << endl;
    cout<< &v2[0] << "\t" << *it2 << endl;

    v1.swap(v2);

    cout<< &v1[0] << "\t" << *it1 << endl;
    cout<< &v2[0] << "\t" << *it2 << endl;
}   

运行结果为:

0x5e0fa8                1   //*it1
0x5e1168 //v2[0]的地址 2
0x5e1168 //v1[0]的地址 1   //*it1不变
0x5e0fa8                2

可以看到swap前后,*vt1和*vt2没有改变,但是&v1[0]和&v2[0]却发生了改变。
实际上,交换操作的过程是这样的:类对象地址不变,但是begin()操作指向的首地址改变,&v1[0]等效于begin操作,也改变。
而迭代器指向的是“地址”,没有改变,所以会产生这样的效果。下面这一段详细的程序可以验证:

    vector v1(10,1);
    vector v2(100,2);
    vector::iterator it1 = v1.begin();
    vector::iterator it2 = v2.begin();
    cout<< &v1 << "\t" << &v1[0] << "\t" <<  *it1 << "\t" << *v1.begin() << endl;
    cout<< &v2 << "\t" << &v2[0] << "\t" << *it2 << "\t" << *v2.begin() << endl;

    v1.swap(v2);

    cout<< &v1 << "\t" << &v1[0] << "\t" <<  *it1 << "\t" << *v1.begin() << endl;
    cout<< &v2 << "\t" << &v2[0] << "\t" << *it2 << "\t" << *v2.begin() << endl;

注意:这里&v1和&v1[0]不是同一个地址,前者是对象地址,后者是第一个元素地址。和数组是不同的。
输出结果为:

0x22fee0                0x5e0fa8                1   //*it1      1 //*v1.begin()
0x22fed4 //对象地址     0x5e1168 //v2[0]的地址 2               2
0x22fee0                0x5e1168 //v1[0]的地址 1   //*it1不变    2 //*v1.begin()改变了  
0x22fed4 //对象地址不变   0x5e0fa8                2               1  

从上述实验可以看到,swap前后,确实只有begin的地址被互换了。
那么,只互换begin,整个vector内容可以互换吗?答案是肯定的,vector的访问是基于首地址+偏移的,类似于数组。
所以如果将首地址互换,vector访问到的所有元素都互换了。用示意图来说明:

                 ________   
    &v1---->    |__ee0___|  
&v1[0](begin)-->|__fa8___|<--it1     

                 ________   
    &v2---->    |__ed4___|  
&v2[0](begin)-->|__168___|<--it2   

交换后:

                    ________    
        &v1------->|__ee0___|  
&v1[0](begin)-| |->|__fa8___|<--it1       
   ___________|_|
  |           |   ________  
  |     &v2---|->|__ed4___|  
&v2[0](begin) |->|__168___|<--it2   

图画的不好,诸位看客见谅。
也许图画和实验都说明不了问题,因为这些都是表象,最后还是要凭借标准和源码来说话。所谓:源码面前,了无秘密。
下面这一段代码摘抄自stl_vector.h,我们主要关注begin和swap操作。

/**
   *  Returns a read/write iterator that points to the first
   *  element in the %vector.  Iteration is done in ordinary
   *  element order.
   */
  iterator
  begin() _GLIBCXX_NOEXCEPT
  { return iterator(this->_M_impl._M_start); }  

可见begin操作是获取Mimpl.Mstart这个值。

void _M_swap_data(_Vector_impl& __x)
{
  std::swap(_M_start, __x._M_start);
  std::swap(_M_finish, __x._M_finish);
  std::swap(_M_end_of_storage, __x._M_end_of_storage);
}  

swap操作也是对Mstart这个值进行处理。可见推理基本正确。这里只是简略分析,如有错讹,还望高手及时指出,不胜感激。

本文的分析到此结束。

致谢

感谢swm同学在分析过程中的帮助。


  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值