C++ Vector swap操作前後迭代器為何不失效
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迭代器失效的問題C++
- C++ vector容器的swap方法C++
- C++ vector容器的swap方法(容器互換)C++
- STL 的 erase() 陷阱-迭代器失效總結
- C++ STL之迭代器C++
- C++ STL迭代器(iterator)C++
- C++ Vector怎麼樣釋放記憶體,通過swap()函式C++記憶體函式
- vector——C++C++
- C++ sort vector<vector<int> > or vector<MyClass> 容器的排序C++排序
- C++中cbegin迭代器學習C++
- C++ STL -- vectorC++
- C++ Vector fundamentalC++
- 【計算機本科補全計劃】C++ Primer:String Vector標準庫及迭代器的使用計算機C++
- 行為型:迭代器模式模式
- 談談 C++ STL 中的迭代器C++
- c/c++ 標準庫 迭代器(iterator)C++
- c++ insert iterators 插入型迭代器C++
- C++的vector容器C++
- C++之vector容器C++
- C++:vector assignC++
- 行為型模式:迭代器模式模式
- C++ 迭代器運算子 箭頭運算子->C++
- 【C++】迭代器與二分查詢C++
- C++中vector*和vector有什麼區別C++
- c++ vector用法詳解C++
- C++【vector】用法和例子C++
- C++ 容器vector的使用C++
- c++ std::vector 切記C++
- C++ Vector資料插入C++
- C++ STL學習——vectorC++
- c++ vector刪除元素C++
- C++中vector<int>& numsC++
- c/c++ 標準庫 vectorC++
- C++ ——vector陣列筆記C++陣列筆記
- C++學習之路(vector::clear和vector::erase的區別)C++
- 二叉樹:前中後序迭代方式統一寫法二叉樹
- 設計模式-行為篇(迭代器模式)設計模式
- Git為何不用C++開發:Linux之父痛貶C++的經典郵件GitC++Linux