訪問元素

std::vector有兩種主要的訪問元素的方法

基於索引的訪問:

這可以使用下標運算子 [] 或成員函式 at() 來完成

兩者都返回對 std::vector 中相應位置的元素的引用(除非它是一個 vector<bool> ),因此它既可以被讀取也可以被修改(如果向量不是 const)。

[]at() 的不同之處在於 [] 不能保證執行任何邊界檢查,而 at() 確實如此。訪問 index < 0index >= size 的元素是 []未定義行為 ,而 at() 丟擲了一個 std::out_of_range異常。

注意: 為清楚起見,下面的示例使用 C++ 11 樣式初始化,但運算子可以與所有版本一起使用(除非標記為 C++ 11)。

Version => C++ 11
std::vector<int> v{ 1, 2, 3 };
// using []
int a = v[1];    // a is 2
v[1] = 4;        // v now contains { 1, 4, 3 }

// using at()
int b = v.at(2); // b is 3
v.at(2) = 5;     // v now contains { 1, 4, 5 }
int c = v.at(3); // throws std::out_of_range exception

因為 at() 方法執行邊界檢查並且可以丟擲異常,所以它比 [] 慢。這使得 [] 成為首選程式碼,其中操作的語義保證索引在邊界內。在任何情況下,對向量元素的訪問都是在恆定時間內完成的。這意味著訪問向量的第一個元素與訪問第二個元素,第三個元素等具有相同的成本(及時)。

例如,考慮這個迴圈

for (std::size_t i = 0; i < v.size(); ++i) {
    v[i] = 1;
}

在這裡我們知道索引變數 i 總是處於邊界內,因此在每次呼叫 operator[] 時檢查 i 是否在界限內會浪費 CPU 週期。

front()back() 成員函式允許向量的第一和最後的元件容易參考存取,分別。這些位置經常使用,特殊訪問器比使用 [] 的替代方案更具可讀性:

std::vector<int> v{ 4, 5, 6 }; // In pre-C++11 this is more verbose

int a = v.front();   // a is 4, v.front() is equivalent to v[0]
v.front() = 3;       // v now contains {3, 5, 6}
int b = v.back();    // b is 6, v.back() is equivalent to v[v.size() - 1]
v.back() = 7;        // v now contains {3, 5, 7}

注意 : 在空向量上呼叫 front()back()未定義的行為 。在呼叫 front()back() 之前,你需要使用 empty()成員函式(檢查容器是否為空)來檢查容器是否為空。使用’empty()‘測試空向量的一個簡單示例如下:

int main ()
{
  std::vector<int> v;
  int sum (0);

  for (int i=1;i<=10;i++) v.push_back(i);//create and initialize the vector

  while (!v.empty())//loop through until the vector tests to be empty
  {
     sum += v.back();//keep a running total
     v.pop_back();//pop out the element which removes it from the vector
  }

  std::cout << "total: " << sum << '\n';//output the total to the user

  return 0;
}

上面的例子建立了一個帶有 1 到 10 的數字序列的向量。然後它將向量的元素彈出,直到向量為空(使用’empty()’)來防止未定義的行為。然後計算向量中的數字之和並顯示給使用者。

Version => C++ 11

data() 方法返回指向由 std::vector 使用的原料記憶體在內部儲存它的元件。在將向量資料傳遞給需要 C 樣式陣列的遺留程式碼時,最常使用此方法。

std::vector<int> v{ 1, 2, 3, 4 }; // v contains {1, 2, 3, 4}
int* p = v.data(); // p points to 1
*p = 4;            // v now contains {4, 2, 3, 4}
++p;               // p points to 2
*p = 3;            // v now contains {4, 3, 3, 4}
p[1] = 2;          // v now contains {4, 3, 2, 4}
*(p + 2) = 1;      // v now contains {4, 3, 2, 1}
Version < C++ 11

在 C++ 11 之前,可以通過呼叫 front() 並獲取返回值的地址來模擬 data() 方法:

std::vector<int> v(4);
int* ptr = &(v.front()); // or &v[0]

這是有效的,因為向量總是保證將它們的元素儲存在連續的記憶體位置,假設向量的內容不會覆蓋一元 operator&。如果是這樣,你將不得不在 pre-C++ 11 中重新實現 std::addressof 。它還假定向量不為空。

迭代器:

迭代器在“Iterating over std::vector”和 Iterators 文章中有更詳細的解釋。簡而言之,它們的行為類似於向量元素的指標:

Version => C++ 11
std::vector<int> v{ 4, 5, 6 };

auto it = v.begin();
int i = *it;        // i is 4
++it; 
i = *it;            // i is 5
*it = 6;            // v contains { 4, 6, 6 }
auto e = v.end();   // e points to the element after the end of v. It can be 
                    // used to check whether an iterator reached the end of the vector:
++it; 
it == v.end();      // false, it points to the element at position 2 (with value 6)
++it;
it == v.end();      // true

std::vector<T> 的迭代器實際上標準的 38s 符合標準,但大多數標準庫不這樣做。不這樣做既改善了錯誤訊息,又捕獲了非可移植程式碼,並且可用於通過非釋出版本中的除錯檢查來檢測迭代器。然後,在釋出版本中,圍繞底層指標的類被優化掉。

你可以將引用或指標持久儲存到向量的元素以進行間接訪問。除非你在 vector 中的元素之前或之前新增/刪除元素,否則這些對 vector 中元素的引用或指標保持穩定並且訪問仍然定義,或者你導致 vector 容量發生變化。這與使迭代器無效的規則相同。

Version => C++ 11
std::vector<int> v{ 1, 2, 3 };
int* p = v.data() + 1;     // p points to 2
v.insert(v.begin(), 0);    // p is now invalid, accessing *p is a undefined behavior.
p = v.data() + 1;          // p points to 1
v.reserve(10);             // p is now invalid, accessing *p is a undefined behavior.
p = v.data() + 1;          // p points to 1
v.erase(v.begin());        // p is now invalid, accessing *p is a undefined behavior.