HOWTO · C++

如何在 C++ 中遍歷向量

了解遍歷 C++ 向量的四種方法:索引迴圈、基於範圍的迴圈、迭代器和 `std::for_each`。

本頁內容

若要逐一造訪 std::vector 中的所有元素,使用基於範圍的 for 迴圈最直接。需要元素位置時選用索引迴圈;演算法需要迭代器範圍時使用迭代器;若要對每個元素套用可呼叫物件,則可使用 std::for_each。以下範例以 C++14 為目標。

使用索引 for 迴圈遍歷向量

索引迴圈能同時取得元素及其位置。迴圈標頭中的三個子句以分號分隔:先初始化索引,再於索引小於 size() 時繼續迴圈,最後遞增索引。這個完整的 C++14 範例展示了此模式;此處不宣稱有執行結果。

#include <cstddef>
#include <iostream>
#include <vector>

int main() {
    const std::vector<int> values = {45, 54, 87, 78};

    for (std::size_t i = 0; i < values.size(); ++i) {
        std::cout << values[i] << ' ';
    }
    std::cout << '\n';
}

此範例將 values 宣告為 const,因此只能讀取:可以使用 values[i] 查看指定索引的元素,但不能修改它。若要更新某個位置,請將向量宣告為可修改(例如 std::vector<int> values = {45, 54, 87, 78};),並在迴圈中指定 values[i] 的值。條件 i < values.size() 可確保每個索引都落在向量有效的元素位置內。當你需要元素的位置時,例如要將數值與另一個序列配對,或更新指定位置,索引存取就很實用。如果只是依序讀取或修改每個值,基於範圍的迴圈通常更簡潔,因為不必自行維護索引。

size() 回傳元素數量,而索引從零開始;因此,向量有元素時,最後一個元素的索引是 size() - 1。使用嚴格小於條件可避免迴圈把元素數量本身當成索引。將此迴圈改用於其他元素型別時,索引型別應與向量的大小型別相容。

使用基於範圍的 for 迴圈

基於範圍的 for 迴圈會逐一造訪每個元素,不必維護索引。使用 const auto& 可在不複製元素的情況下讀取;若迴圈需要更新原始值,則使用 auto&。這個 C++14 範例展示了兩種形式;此處不宣稱有執行結果。

#include <iostream>
#include <vector>

int main() {
    std::vector<int> values = {45, 54, 87, 78};

    for (const auto& value : values) {
        std::cout << value << ' ';
    }
    std::cout << '\n';

    for (auto& value : values) {
        value += 1;
    }
}

第一個迴圈將每個 value 綁定為唯讀參考。第二個迴圈則綁定可修改的參考,因此指定值會更新 values 中對應的元素。如果只需要檢視值,建議使用 const 形式,讓讀取意圖更清楚。

使用迭代器遍歷向量

當介面接受一對位置,或你需要明確控制容器的移動方式時,可以使用迭代器。從 begin() 開始,直到 end() 為止。end() 迭代器表示最後一個元素之後的位置;請解參考目前的迭代器,絕不要解參考 end() 本身。這個 C++14 範例展示了遍歷方式;此處不宣稱有執行結果。

#include <iostream>
#include <vector>

int main() {
    const std::vector<int> values = {45, 54, 87, 78};

    for (std::vector<int>::const_iterator it = values.begin();
         it != values.end(); ++it) {
        std::cout << *it << ' ';
    }
    std::cout << '\n';
}

!= 比較運算表示常見的半開迭代器範圍 [begin(), end())。使用 const_iterator 可以讀取每個元素,同時避免透過該迭代器修改元素。

同一對迭代器也可以傳給標準演算法,因此,若程式碼已使用迭代器範圍,明確遍歷迭代器就很實用。對唯讀向量而言,begin() 和 end() 會提供常數迭代器;對可修改的向量而言,它們則會提供可在解參考時更新元素的迭代器。

使用 std::for_each 套用操作

<algorithm> 中的 std::for_each 會對半開範圍內的每個元素套用可呼叫物件;範圍從 begin() 開始,到 end() 為止,但不包含 end()。使用 lambda 可以將操作與遍歷邏輯放在一起。這個 C++14 範例透過可修改參考,將每個值加上十;此處不宣稱有執行結果。

#include <algorithm>
#include <iostream>
#include <vector>

int main() {
    std::vector<int> values = {1, 2, 3};

    std::for_each(values.begin(), values.end(), [](int& value) {
        value += 10;
    });

    for (const auto& value : values) {
        std::cout << value << ' ';
    }
    std::cout << '\n';
}

若可呼叫物件只讀取元素,請使用 const int& 作為 lambda 參數。若只是單純逐一造訪,基於範圍的迴圈通常更易閱讀;若將操作作為可呼叫物件傳遞更符合周邊程式碼,就選用 std::for_each。這四種方法都會遍歷同一個向量中的元素,差別在於如何取得位置及套用操作。