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同学在分析过程中的帮助。