簡體   English   中英

std::vector::swap 實際上是做什么的?

[英]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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM