您的位置:首页 > 编程语言 > C语言/C++

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

2013-12-01 11:41 363 查看

1 swap操作

swap是STL泛型操作的一种。这种操作的时间复杂度极低,用于两个容器内容的交换。

例如定义vector vi1和vi2, vi1.swap(vi2),就将vi1和vi2的内容交换了。

2 问题

iterator实际上是一种指针,可以指向容器的任意位置。例如vector::iterator it1 = vi1.begin();

这两个操作本身很简单,但是怪异的一点是swap前后,迭代器不失效,原来指向什么内容,swap后还是指向什么内容。

例如下面这段代码:

#include <iostream>
#include <vector>
using namespace std;
int main()
{
    vector<int> v1(10,1);
    vector<int> v2(100,2);
    vector<int>::iterator it1 = v1.begin();
    vector<int>::iterator it2 = v2.begin();
    cout<< "&v1[0]=" << &v1[0] << "\t\t" << "*it1=" <<*it1 << endl;
    cout<< "&v2[0]=" << &v2[0] << "\t\t" << "*it1=" <<*it2 << endl;
	
    v1.swap(v2);
    cout<<"after v1.swap(v2)"<<endl;
    cout<< "&v1[0]=" << &v1[0] << "\t\t" << "*it1=" <<*it1 << endl;
    cout<< "&v2[0]=" << &v2[0] << "\t\t" << "*it1=" <<*it2 << endl;

	return 0;
}
结果如下:



结果表明:

(1)swap前后,*vt1和*vt2没有改变,但是&v1[0]和&v2[0]却发生了改变。

(2)实际上,交换操作的过程是这样的:

类对象地址不变,但是begin()操作指向的首地址改变,&v1[0]等效于begin操作,也改变。

而迭代器指向的是“地址”,没有改变,所以会产生这样的效果。

下面这一段详细的程序可以验证:

#include <iostream>
#include <vector>
using namespace std;

int main()
{

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

    cout<<"v2的属性:"<<endl;
    cout<< "&v2=" << &v2 << "\n" 
	<< "&v2[0]=" << &v2[0] << "\t\t" << "v2[0]=" <<	v2[0]  << "\n"
	<< "it2="    << it2    << "\t\t" << "*it2="  << *it2   << "\n"
	<<"v2.begin()="<<v2.begin()  << "\t" << "*v2.begin()="<<*v2.begin()
	<< endl;
	
    v1.swap(v2);
	
    cout<<"---------after v1.swap(v2)----------"<<endl;
    cout<<"v1的属性:"<<endl;
    cout<< "&v1=" << &v1 << "\n" 
	<< "&v1[0]=" << &v1[0] << "\t\t" << "v1[0]=" << v1[0] << "\n"
	<< "it1="    << it1    << "\t\t" << "*it1="  << *it1  << "\n"
	<<"v1.begin()="<<v1.begin()  << "\t" << "*v1.begin()="<<*v1.begin()
	<< endl;
	
    cout<<"v2的属性:"<<endl;
    cout<< "&v2=" << &v2 << "\n" 
	<< "&v2[0]=" << &v2[0] << "\t\t" << "v2[0]=" << v2[0]  << "\n"
	<< "it2="    << it2    << "\t\t" << "*it2="  << *it2   << "\n"
	<<"v2.begin()="<<v2.begin()  << "\t" << "*v2.begin()="<<*v2.begin()
	<< endl;
    return 0;
}
结果如下:



结果表明:

(1)这里&v1和&v1[0]不是同一个地址,前者是对象地址,后者是第一个元素地址。和数组是不同的。

(2)swap前后,确实只有begin的地址被互换了。

(3)只互换begin,整个vector内容可以互换吗?答案是肯定的,vector的访问是基于首地址+偏移的,类似于数组。

所以如果将首地址互换,vector访问到的所有元素都互换了。

内存示意图:



3 原理

3.1 vector类

vector内部是维护一个动态数组,大概样子是这样:

class vector

{

T* begin;

T* finish;

T* capacity;

};

vector::iterator虽说不一定是用指针实现,但是一定保存了数组中元素的真实内存地址

(其实也不是一定,实现也可以保存begin和偏移量,但这么做明显太复杂,实际中都是直接保存元素地址的)。

vector使用swap的时候,其实交换的是双方begin, finish, capacity这三个指针的值。

在这个过程中,所有从该vector创建的iterator本身都不会发生任何改变(打个比方就是你换一个手机号,那些存了你以前手机号的人手头的电话号码并不会随之发生改变,除非你告诉他们)。

因为iterator没有改变,所以你打印iterator本身的地址时,得到的仍旧是原来的值。

另外,动态数组本身并没有被销毁,只是改变了所属。

所以实际两个vector中所有元素的物理地址都没有改变,iterator指向的依然是原来的元素。

3.2 swap函数

下面这一段代码摘抄自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操作是获取_M_impl._M_start这个值。

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这个值进行处理。

3.3 模拟仿真

下面是一种常见的迭代器实现方案的简化代码:

template <typename T>

struct base_iterator

{

base_iterator(T* p):m_ptr(p) {}

T* operator->() { return m_ptr; }

// 剩下的若干重载运算符。。。

T* m_ptr;

};

template <typename T>

class vector

{

typedef base_iterator<T> iterator;

T* begin;

T* finish;

T* capacity;

iterator begin() { return iterator(begin); }

};

获取begin()迭代器实际上就是用begin指针创建了一个iterator对象而已。

4 参考文献

[1]http://www.software8.co/wzjs/cpp/5047.html
作者:henrystark

[2]http://bbs.csdn.net/topics/390410638?page=1
作者:cnm_1314 和 tofu_
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐