Strings and Vectors

Strings and Vectors

Work with dynamic text and lists using the most common standard library types.

Strings and Vectors

std::string

std::string text = "hello";
text += " world";
auto pos = text.find("world");
auto prefix = text.substr(0, 5);

find and substr cover a large amount of practical text work before you need more advanced parsing tools.

Strings grow dynamically and manage their own memory, which makes them the normal choice for text in modern C++.

std::vector

std::vector<int> scores{10, 20, 30};
scores.push_back(40);

Vectors are the default dynamic sequence type because they are contiguous, efficient, and work well with algorithms and views.

Iteration

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

Example: average calculator

#include <iostream>
#include <vector>

int main() {
    std::vector<int> values{4, 8, 15, 16, 23, 42};
    int sum = 0;

    for (int value : values) {
        sum += value;
    }

    std::cout << "Average: " << sum / values.size() << '\n';
}

String splitting sketch

std::istringstream input{"Ada,Bjarne,Grace"};
std::string token;

while (std::getline(input, token, ',')) {
    std::cout << token << '\n';
}

This is a simple way to break delimited text into pieces before you move to more specialized parsing tools.

Key lessons

Growth and capacity

std::vector<std::string> names;
names.reserve(100);

Reserve capacity when you know approximate growth. It reduces reallocations and iterator invalidation.

for (std::string line; std::getline(std::cin, line); ) {
    names.push_back(line);
}

If you expect many insertions, reserving up front can avoid repeated reallocations as the vector grows.

Access and bounds

int first = values[0];
int checked = values.at(0);

Use [] when you already know the index is valid. Use .at() when you want bounds checking and are willing to pay for that safety.

String views and ranges

std::string title = "Modern C++";
std::string_view view = title;

Use std::string_view for read-only access, but remember it does not own the string.

Iterator and reference invalidation

std::vector<int> numbers{1, 2, 3};
int* ptr = &numbers[0];
numbers.push_back(4);

After growth, pointers, references, and iterators into a vector may no longer be valid if reallocation happened. That is one of the most important vector rules to remember.

Practical mini-program

#include <iostream>
#include <sstream>
#include <string>
#include <vector>

int main() {
    std::string line{"10 20 30 40"};
    std::istringstream input{line};
    std::vector<int> values;
    values.reserve(4);

    for (int value{}; input >> value; ) {
        values.push_back(value);
    }

    std::cout << "count = " << values.size() << '\n';
}

This pattern shows how strings and vectors often work together: text input is parsed into typed values, then stored for later processing.

Another useful string pattern

std::string path = "content/tutorials/getting-started.md";
if (path.ends_with(".md")) {
    std::cout << "markdown file\n";
}

Modern string operations such as starts_with and ends_with make many small text checks much clearer.

Next modern step

Once vectors and strings feel comfortable, start replacing manual loops with algorithms such as std::ranges::sort, std::ranges::find, and std::ranges::transform.

Example in practice

#include <string>
#include <vector>

int main() {
    std::vector<std::string> names{"Ada", "Bjarne"};
    names.push_back("Grace");
    return static_cast<int>(names.size());
}

Try this variation

Reserve space before growth and pass read-only text as `std::string_view`. That introduces both allocation control and borrowing in one small step.

#include <string_view>
#include <vector>

void add_name(std::vector<std::string>& names, std::string_view name) {
    names.emplace_back(name);
}