Vectors in C++

    Atul Kabra4 min readUpdated

    A std::vector is C++'s resizable array — a sequence that grows and shrinks as you add or remove elements, while still giving you fast index access. It is the container you will reach for most often. Unlike a raw array, a vector knows its own size, manages its own memory, and frees that memory automatically. For almost every "I need a list of things" situation, std::vector is the right starting point.

    Here is how to use one.

    Creating and filling a vector

    Include <vector>, then create and add elements with push_back.

    #include <iostream>
    #include <vector>
    #include <string>
    
    int main() {
        std::vector<std::string> names;   // empty vector of strings
    
        names.push_back("Asha");          // add to the end
        names.push_back("Ravi");
        names.push_back("Meera");
    
        std::cout << "Count: " << names.size() << "\n";  // 3
        std::cout << "First: " << names[0] << "\n";       // Asha
    }
    

    You can also initialise with values directly: std::vector<int> marks{72, 95, 60};.

    Looping over a vector

    The cleanest loop is the range-based for, which visits each element in turn.

    #include <iostream>
    #include <vector>
    
    int main() {
        std::vector<int> marks{72, 95, 60, 88};
    
        // read each element by const reference (no copy)
        for (const int& m : marks) {
            std::cout << m << " ";
        }
        std::cout << "\n";
    
        // index loop when you need the position
        for (std::size_t i = 0; i < marks.size(); ++i) {
            std::cout << "marks[" << i << "] = " << marks[i] << "\n";
        }
    }
    

    Use std::size_t (not int) for the index, because size() returns an unsigned type.

    Safe access with at()

    marks[5] on a 4-element vector is undefined behaviour — it reads past the end with no warning. at() does the same job but throws an exception if the index is out of range, which is far easier to debug.

    #include <vector>
    #include <iostream>
    
    int main() {
        std::vector<int> v{10, 20, 30};
        std::cout << v.at(1) << "\n";    // 20
    
        try {
            std::cout << v.at(99) << "\n";  // throws std::out_of_range
        } catch (const std::out_of_range& e) {
            std::cout << "Caught: " << e.what() << "\n";
        }
    }
    

    Want to learn this properly?

    Join the waitlist for our courses — beginner-friendly, project-first classes in Jalgaon.

    Browse courses

    Adding and removing

    std::vector<int> v{1, 2, 3};
    v.push_back(4);        // -> 1 2 3 4
    v.pop_back();          // remove last -> 1 2 3
    v.clear();             // remove everything -> empty
    bool isEmpty = v.empty();   // true
    

    For building large vectors, call v.reserve(n) first if you know the final size — it avoids repeated re-allocations and is a cheap speed win.

    Removing a specific value (not just the last one) takes a small two-step idiom called erase-remove, or in C++20 the one-liner std::erase:

    #include <vector>
    
    int main() {
        std::vector<int> v{1, 2, 3, 2, 4, 2};
        std::erase(v, 2);   // C++20: removes every 2 -> 1 3 4
    }
    

    Before C++20 you would write v.erase(std::remove(v.begin(), v.end(), 2), v.end()); — the modern std::erase does the same thing far more readably.

    Why prefer vectors over raw arrays

    A raw array (int a[100];) has a fixed size, does not know how big it is, and gives no bounds checking. A std::vector resizes on demand, reports its size(), frees its memory automatically, and offers at() for checked access. There is virtually no situation in modern C++ where a raw owning array beats a vector.

    Common mistakes

    • Indexing out of range with []. v[v.size()] is undefined behaviour. Use at() while learning, and never index past size() - 1.
    • Using int for the loop index. size() is unsigned; comparing it against a signed int triggers warnings and can loop incorrectly. Use std::size_t.
    • Holding an iterator or reference across a push_back. Growing a vector may move its storage, invalidating old references and iterators.
    • Copying a large vector by accident. Passing std::vector by value copies every element. Pass by const& for read-only parameters.

    FAQ

    What is the difference between [] and at()? Both access an element by index. [] does no bounds checking (faster, but undefined behaviour if out of range), while at() throws std::out_of_range on a bad index.

    Should I use a vector or a raw array? Prefer std::vector in almost every case. It manages its own memory, knows its size, and is just as fast for indexed access.

    Keep learning

    Practise with real data structures and mentors in Jalgaon — join the waitlist for our C++ Programming course.

    Want to learn this properly?

    Join the waitlist for our courses — beginner-friendly, project-first classes in Jalgaon.

    Browse courses
    Atul Kabra

    Founder, Infoplanet

    Atul Kabra founded Infoplanet in 2001 and has spent over two decades teaching programming — C, C++, Java, databases and more — to students across Maharashtra.

    Related guides