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_
相关文章推荐
- C++ Vector swap操作前后迭代器为何不失效
- 容器大小的改变以及容器操作可能使迭代器失效、vector对象的容量变化
- C++中 vector结合迭代器实现增删操作!
- 实战c++中的vector系列--可怕的迭代器失效之二(删除vector中元素)
- C++ vector 迭代器失效原因
- C++中防止STL中迭代器失效——map/set等关联容器——vector/list/deque等序列容器—如何防止迭代器失效—即erase()的使用
- 实战c++中的vector系列--可怕的迭代器失效之二(删除vector中元素)
- STL中vector的删除操作引起迭代器失效和空间重新配置
- C++中防止STL中迭代器失效——map/set等关联容器——vector/list/deque等序列容器—如何防止迭代器失效—即erase()的使用
- 关于vector的erase操作后,迭代器失效问题
- vector的插入操作使原迭代器全部失效,删除操作使其后面的原迭代器失效
- C++中防止STL中迭代器失效__map/set等关联容器vector/list/deque等序列容器_如何防止迭代器失效_即erase()的使用
- C++注意事项-----------删除,添加等操作可能会使当前迭代器失效
- 【C++学习】迭代器的使用:vector的操作---ShinePans
- C++中防止STL中迭代器失效__map/set等关联容器vector/list/deque等序列容器_如何防止迭代器失效_即erase()的使用
- C++语法基础--顺序容器(二)--容器常用操作,迭代器失效总结
- 实战c++中的vector系列--可怕的迭代器失效(vector重新申请内存)
- 实战c++中的vector系列--可怕的迭代器失效(vector重新申请内存)
- C++ 中Vector常用基本操作
- C++ 指针操作vector容器