[英]What does std::vector::swap actually do?
觸發這個問題的是一些代碼:
std::vector<int> x(500);
std::vector<int> y;
std::swap(x,y);
而且我想知道交換兩者是否需要兩倍於x
需要的 memory 數量。
在cppreference上,我找到了std::vector::swap
(這是最后一行有效調用的方法):
將容器的內容與其他容器的內容交換。 不對單個元素調用任何移動、復制或交換操作。
所有迭代器和引用仍然有效。 過去的迭代器無效。
而現在我比以前更加困惑。 當std::vector::swap
不移動、復制或交換元素時,它實際上做了什么? 迭代器如何保持有效?
這是否意味着這樣的東西是有效的代碼?
std::vector<int> x(500);
std::vector<int> y;
auto it = x.begin();
std::swap(x,y);
std::sort(it , y.end()); // iterators from different containers !!
vector
在內部存儲(至少)指向元素實際存儲的指針、大小和容量。 † std::swap
只是交換指針、大小和容量(以及輔助數據,如果有的話); 沒有將 memory 加倍或復制元素,因為x
中的指針變為y
中的指針,反之亦然,沒有分配任何新的 memory。
vector
的迭代器通常是輕量級包裝器,圍繞指向底層分配的 memory 的指針(這就是容量更改通常使迭代器無效的原因), 因此在swap
之前為x
生成的迭代器無縫地在swap
之后繼續引用y
; 您對sort
的示例使用是合法的,並且對y
進行了排序。
如果您想在不交換存儲的情況下交換元素本身(這是一項更昂貴的操作,但會留下現有的迭代器用於x
引用x
),您可以使用std::swap_range
,但這是一個相對不常見的用例。 memory 的使用取決於底層 object 的swap
實現; 默認情況下,它通常會涉及一個臨時的,但僅限於一次交換的一個對象,而不是整個一個vector
。
†根據評論,它可以等效地使用指向已用空間末尾和容量末尾的指針,但是這兩種方法在邏輯上都是等效的,並且只是進行了微優化以支持略有不同的預期用例; 存儲所有指針優化迭代器的使用(一個合理的選擇),而存儲size_type
優化 .size( .size()
/ .capacity()
調用。
我會寫一個玩具矢量。
struct toy_vector {
int * buffer = 0;
std::size_t valid_count = 0;
std::size_t buffer_size = 0;
int* begin() { return buffer; }
int* end() { return buffer+valid_count; }
std::size_t capacity() const { return buffer_size; }
std::size_t size() const { return valid_count; }
void swap( toy_vector& other ) {
std::swap( buffer, other.buffer );
std::swap( valid_count, other.valid_count );
std::swap( buffer_size, other.buffer_size );
}
基本上就是這樣。 我將實現一些方法,以便您看到我們有足夠的工具可以使用:
int& operator[](std::size_t i) { return buffer[i]; }
void reserve(int capacity) {
if (capacity <= buffer_size)
return;
toy_vector tmp;
tmp.buffer = new int[capacity];
for (std::size_t i = 0; i < valid_count; ++i)
tmp.buffer[i] = std::move(buffer[i]);
tmp.valid_count = valid_count;
tmp.buffer_size = capacity;
swap( tmp );
}
void push_back(int x) {
if (valid_count+1 > buffer_size) {
reserve( (std::max)((buffer_size*3/2), buffer_size+1) );
buffer[valid_count] = std::move(x);
++valid_count;
}
// real dtor is more complex.
~toy_vector() { delete[] buffer; }
};
實際的向量有異常安全問題,更多關注 object 生命周期和分配器(我使用的是整數,所以不要關心我是否正確構造/銷毀它們),並且可能存儲 3 個指針而不是指針和 2 個大小。 但是交換這 3 個指針就像交換指針和 2 個大小一樣容易。
實向量中的迭代器往往不是原始指針(但它們可以是)。 正如您在上面看到的,當您執行a.swap(b)
時,指向向量a
原始指針迭代器變成指向向量b
的原始指針迭代器; 非原始指針向量迭代器基本上是花哨的包裝指針,並且必須遵循相同的語義。
C++ 標准沒有明確規定一個看起來像這樣的實現,但它基於一個看起來像這樣的實現,並且它隱含地需要一個幾乎與這個相同的實現(我相信有人可以想出一個聰明的標准看起來不像這樣的兼容向量;但是我見過的每個標准庫中的每個向量都看起來像這樣。)
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.