Modern C++ code should reach for standard library types before raw arrays and manual memory.
For text, use std::string. For a growable sequence, use std::vector.
std::string
std::string owns text:
std::string name{"Maya"};
name += " Chen";
The string manages its own memory. You do not allocate or free character buffers manually.
Pass strings by const reference when reading:
void print_uppercase(const std::string& text);
Return strings by value when producing new text:
std::string make_title(const std::string& name) {
return "Report: " + name;
}
std::vector
std::vector<T> stores a dynamic sequence of T values:
std::vector<int> scores{80, 91, 77};
scores.push_back(88);
Use vector as the default sequence container. It stores elements contiguously, works well with caches, and has a simple ownership model.
Indexing and bounds
operator[] is fast but unchecked:
scores[0] = 95;
If the index is out of range, behavior is undefined.
Use .at() when you want checked access:
scores.at(0) = 95;
An invalid .at() access throws std::out_of_range.
Range-based loops
Read without copying:
for (const auto& score : scores) {
std::cout << score << "\n";
}
Mutate intentionally:
for (auto& score : scores) {
score += 5;
}
Copy only when you want a copy:
for (auto score : scores) {
std::cout << score << "\n";
}
For int, copying is cheap. For large objects, prefer const auto& when reading.
Capacity
A vector may allocate more storage than it currently uses. size() is the number of elements. capacity() is how many elements can fit before reallocation.
If you know roughly how many elements you will add, reserve capacity:
std::vector<std::string> names;
names.reserve(1000);
This can avoid repeated allocations.
What to carry forward
- use
std::stringfor owned text - use
std::vectoras the default sequence container []is unchecked;.at()checks bounds- range-based loops make element access clear
- reserve vector capacity when growth is predictable
Next, you will compare other standard containers and when to choose them.