A real, owning, growable text type — plus the lightweight non-owning "window" you pass around to avoid copying it. When to use which, and the dangling trap to dodge.
🎯 Handle text the modern way — nochar*, no leaks
std::string owns its characters (think "vector<char> with text powers"). std::string_view (C++17) is a non-owning window into characters someone else owns — cheap to pass, but it must never outlive the buffer it points at.
Forget char*, manual malloc, and strcpy. std::string owns a growable buffer of characters, manages the memory for you (RAII), and gives you concatenation, search, and slicing as methods.
#include <string> std::string s = "hello"; s += ", world"; // concatenate in place → "hello, world" s.size(); // 12 (length() is a synonym) s[0]; // 'h' — like a vector of char s.substr(7); // "world" — slice from index 7 s.substr(0, 5); // "hello" — slice [0, 5) s.find("world"); // 7 (or std::string::npos if absent) std::string greet = "hi " + name; // build by + std::cout << s.c_str(); // borrow a const char* for an old C API
-std=c++17+).string is basically a sequence of char: size(), [], at(), begin()/end(), range-for, push_back — all the vector vocabulary works, plus text methods.
npos means "not found"find returns the index, or the sentinel std::string::npos if the needle isn't there. Always test if (pos != std::string::npos) before using it.
std::stoi("42") → int; std::stod → double; std::to_string(42) → "42". (For formatted output, std::format — §05.)
Source: cppreference — std::basic_string (std::string is basic_string<char>).
| You want to… | Call |
|---|---|
| length | s.size() / s.length() / s.empty() |
| append | s += t; · s.append(t) · s.push_back('x') |
| slice | s.substr(pos, len) |
| search | s.find(t), rfind, find_first_of (returns index or npos) |
| test prefix/suffix C++20 | s.starts_with(t), s.ends_with(t) |
| replace / insert / erase | s.replace(pos,len,t), s.insert, s.erase |
| compare | a == b, a < b (lexicographic — operators just work) |
| hand to a C API | s.c_str() → null-terminated const char* |
== compares pointers and you need strcmp), std::string overloads ==, <, etc. to compare contents. This is also why string drops straight into a map or set key (Lessons 4–5).Every time you pass a std::string by value, you copy the whole buffer. Often a function only wants to read the characters. std::string_view is a tiny object — just a pointer + length — that looks at characters owned by someone else. Copying a string_view copies two machine words, never the text.
string_view is a cheap "I'm only reading" handle. It owns nothing, so its lifetime must be inside the owner's.#include <string_view> // One signature accepts std::string, char* literal, or a slice — with NO copy: size_t count_vowels(std::string_view text) { size_t n = 0; for (char c : text) if (std::string_view("aeiou").find(c) != std::string_view::npos) ++n; return n; } count_vowels("hello"); // from a literal — no allocation std::string s = "world"; count_vowels(s); // from a std::string — no copy
std::string_view by value. It binds to a std::string, a string literal, or a substring without ever copying — one signature, zero allocations.Source: cppreference — std::string_view.
A string_view owns nothing, so it's only valid while the characters it points at are alive. Two bugs to memorize:
// BUG 1: returning a view of a local string std::string_view bad() { std::string tmp = "oops"; return tmp; // tmp is destroyed on return → view dangles } // BUG 2: a view of a temporary that's already gone std::string_view v = std::string("temp") + "x"; // temporary dies at the ';' std::cout << v; // undefined behavior
string_view is for passing through and reading, not for owning or stashing. If you need to keep the text, store a std::string (it copies and owns).| Situation | Use |
|---|---|
| A read-only text parameter | std::string_view (by value) |
| A class member / something you keep | std::string (owns it) |
| Returning newly built text | std::string (by value) |
| A function that modifies the text | std::string& (mutable reference) |
Source: Core Guidelines on string_view for non-owning read-only parameters.
Concatenating with + and to_string gets ugly fast. std::format brings Python-style {} formatting to C++:
#include <format> std::string msg = std::format("{} has {} points ({:.1f}%)", name, pts, pct); // "Ada has 42 points (87.5%)" — types deduced, no + soup, no stream manip
std::format (and std::print in C++23) is the modern way to build and emit text — type-safe, readable, fast. We'll see it again in the I/O lesson. If your compiler lacks it, the {fmt} library is the identical original.Source: cppreference — std::format.
std::string_view actually hold?s.find("x") returns std::string::npos. That means:string_view of a local std::string a bug?std::string vs std::string_view in one line each?string owns a growable char buffer (manages memory). string_view is a non-owning pointer+length window into chars someone else owns.string_view?std::string, a string literal, or a substring with zero copies.string_view?std::string overloads ==, <, etc. to compare contents. C strings (char*) compare pointers — you need strcmp.std::stoi/stod parse a string to a number; std::to_string goes the other way; std::format builds rich text with {} placeholders.std::string to an old C API?s.c_str() gives a null-terminated const char* viewing the string's buffer (valid until the string is modified/destroyed).Try: "is this string_view safe?" and paste a snippet. Or "show me a tokenizer using find + substr". We'll wire text handling to the containers from here.