SovranCode
Modern C++ Lesson 16
Course contents50 available · 0 planned

Get a program running

01 · C++ Introduction: What Is C++?02 · How to Install a C++ Compiler and Set Up Your Editor03 · C++ Hello World: Your First Program Explained04 · How to Compile and Run C++ Programs05 · C++ Debugging: Warnings and Breakpoints

Work with values and input

06 · C++ Variables: Initialization and Scope07 · C++ Data Types: int, double and Type Conversion08 · C++ const, constexpr and auto Explained09 · C++ Operators: Arithmetic, Comparison and Logic10 · C++ User Input: cin, getline and Validation

Control flow and functions

11 · C++ Conditions: if, else if and else12 · C++ Switch Statement: Cases, break and default13 · C++ For Loop: Syntax and Examples14 · C++ While and Do-While Loops: Examples15 · C++ Functions: Parameters and Return Values

Strings and sequences

16 · C++ Strings: std::string with Examples17 · C++ Vector: std::vector with Examples18 · C++ Arrays: std::array and Bounds Checking19 · C++ Range-Based For Loop and Iterators20 · C++ Vector Practice: Build a Data Summary

Model data with classes

21 · C++ Structs and enum class Explained22 · C++ Classes and Objects: Encapsulation23 · C++ Constructors and Member Initialization24 · C++ Const Member Functions and Composition25 · C++ Copying Objects and the Rule of Zero

Lifetime and resource ownership

26 · C++ Object Lifetime, Destructors and RAII27 · C++ Pointers and References: nullptr and Borrowing28 · C++ unique_ptr: Smart Pointers and Ownership29 · C++ Move Semantics: std::move vs Copying30 · C++ string_view and span: Avoid Dangling Views

Containers and algorithms

31 · C++ map vs unordered_map: Lookup and Examples32 · C++ set, queue and stack: When to Use Each33 · C++ Lambda Expressions and STL Algorithms34 · C++ sort and binary_search: Rules and Complexity35 · C++20 Ranges: Views, filter and transform

Errors, files, and interfaces

36 · C++ optional and variant Explained37 · C++ Exceptions: try, catch and Safe Cleanup38 · C++ File Handling: Read and Write Text Files39 · C++ Input Parsing and Data Validation40 · C++ Polymorphism: Virtual Functions and Interfaces

Generic programming and quality

41 · C++ Templates: Function and Class Examples42 · C++20 Concepts and Template Constraints43 · C++ Unit Testing: Normal and Boundary Cases44 · C++ Sanitizers: Find Memory and Undefined Behavior Bugs45 · C++ Performance: Benchmark Before Optimizing

Build and deliver a complete application

46 · C++ Header Files and Translation Units47 · C++ CMake Tutorial: Build an Executable48 · C++ Dependencies and CTest: Automated Tests49 · C++ Multithreading: Threads and Data Races50 · C++ Command-Line Applications: Help and Release Checks
Modern C++50 available · 0 planned

Get a program running

01 · C++ Introduction: What Is C++?02 · How to Install a C++ Compiler and Set Up Your Editor03 · C++ Hello World: Your First Program Explained04 · How to Compile and Run C++ Programs05 · C++ Debugging: Warnings and Breakpoints

Work with values and input

06 · C++ Variables: Initialization and Scope07 · C++ Data Types: int, double and Type Conversion08 · C++ const, constexpr and auto Explained09 · C++ Operators: Arithmetic, Comparison and Logic10 · C++ User Input: cin, getline and Validation

Control flow and functions

11 · C++ Conditions: if, else if and else12 · C++ Switch Statement: Cases, break and default13 · C++ For Loop: Syntax and Examples14 · C++ While and Do-While Loops: Examples15 · C++ Functions: Parameters and Return Values

Strings and sequences

16 · C++ Strings: std::string with Examples17 · C++ Vector: std::vector with Examples18 · C++ Arrays: std::array and Bounds Checking19 · C++ Range-Based For Loop and Iterators20 · C++ Vector Practice: Build a Data Summary

Model data with classes

21 · C++ Structs and enum class Explained22 · C++ Classes and Objects: Encapsulation23 · C++ Constructors and Member Initialization24 · C++ Const Member Functions and Composition25 · C++ Copying Objects and the Rule of Zero

Lifetime and resource ownership

26 · C++ Object Lifetime, Destructors and RAII27 · C++ Pointers and References: nullptr and Borrowing28 · C++ unique_ptr: Smart Pointers and Ownership29 · C++ Move Semantics: std::move vs Copying30 · C++ string_view and span: Avoid Dangling Views

Containers and algorithms

31 · C++ map vs unordered_map: Lookup and Examples32 · C++ set, queue and stack: When to Use Each33 · C++ Lambda Expressions and STL Algorithms34 · C++ sort and binary_search: Rules and Complexity35 · C++20 Ranges: Views, filter and transform

Errors, files, and interfaces

36 · C++ optional and variant Explained37 · C++ Exceptions: try, catch and Safe Cleanup38 · C++ File Handling: Read and Write Text Files39 · C++ Input Parsing and Data Validation40 · C++ Polymorphism: Virtual Functions and Interfaces

Generic programming and quality

41 · C++ Templates: Function and Class Examples42 · C++20 Concepts and Template Constraints43 · C++ Unit Testing: Normal and Boundary Cases44 · C++ Sanitizers: Find Memory and Undefined Behavior Bugs45 · C++ Performance: Benchmark Before Optimizing

Build and deliver a complete application

46 · C++ Header Files and Translation Units47 · C++ CMake Tutorial: Build an Executable48 · C++ Dependencies and CTest: Automated Tests49 · C++ Multithreading: Threads and Data Races50 · C++ Command-Line Applications: Help and Release Checks
PREVIOUS LESSONC++ Functions: Parameters and Return Values
NEXT LESSONC++ Vector: std::vector with Examples
C++ foundations · Lesson 16 About 60 min with practice

C++ Strings: std::string with Examples

Use C++ std::string for owning text: construction, concatenation, comparison, find, substr and bounds checks. Normalize a small ASCII command without claiming Unicode processing.

Before you begin

Understand initialized values, loops and functions. Compile these complete programs using the editor execution service when available, or your local C++20 toolchain.

What you will learn

  • Distinguish an owning std::string from a literal or borrowed view.
  • Read whole lines and compare text contents.
  • Check positions before indexing or slicing.
  • Handle find returning npos before arithmetic.
  • Normalize only a documented ASCII command alphabet.

A string object owns a sequence of characters

std::string is a standard-library type that owns its character sequence. Include string and initialize an object such as std::string topic{"C++"}. The object tracks its length and manages storage, so you do not need a raw character allocation for a beginner text report. That ownership makes copying a string a value operation with its own contents. It does not imply every later pointer or view into those contents remains valid after changes.

A string literal, an owning string and a borrowed string view are different kinds of things. auto label = "ready" does not create an owning std::string in the ordinary declaration used earlier. Write the library type explicitly when ownership is part of the lesson. std::string_view is useful later for borrowed access, but this introduction keeps ordinary owned values so lifetime and mutation have a simple starting model.

Text has an encoding as well as a storage length. std::string stores char units; when those units contain UTF-8, size counts bytes rather than user-perceived characters. A visible accented character or emoji can require several units. Do not advertise a string length counter as a universal character counter or slice arbitrary Unicode text by byte positions and assume complete symbols are preserved. Our command examples use a deliberately small ASCII vocabulary.

The ASCII text status has six character units indexed zero through five; size is six and is not an element index. Search returns a valid position or npos, and slices must be based on checked positions before use.
For ASCII status, positions 0–5 are elements; size 6 is the past-the-end boundary.
C++ PRACTICE RUNNER

Print an owning text value

Change the label or command independently. This browser example uses only initialized strings and output, not string methods.

#include <iostream>
#include <string>
int main() {
    const std::string label{"Command"};
    const std::string command{"status"};
    std::cout << label << ": " << command << '\n';
    return 0;
}
TERMINAL OUTPUT
Predict the output, edit the source, then select Run code.

C++20 compiler runner: source and input are sent only when you press Run to this website's execution endpoint, not an external vendor. Local preview requires Docker and gcc:14; production requires a separately isolated execution service. Network is disabled; execution is limited to 3 seconds, compilation to 10 seconds, memory to 384 MB and output to 32 KB. Single-file programs only. Don't enter secrets. Edits reset on refresh; Copy your work to keep it.

Compare with the starter's expected output
Command: status

After editing inputs, calculate a new prediction; this reference describes only the starter.

Combine text and compare content deliberately

For string objects, + can concatenate text and += can append to an existing object. The type of the operands matters: two raw string literals are not automatically two std::string objects whose + operation performs concatenation. Start with an owning string or use a form whose overload you can explain. Keeping a prefix, data value and delimiter separate in output is often clearer than building an intermediate string that is never reused.

== compares string contents. Lexicographic comparison orders character sequences rather than interpreting them as numbers: the ASCII text 10 can sort before 2. Case and whitespace also affect equality; Status is not status, and a trailing space remains part of a plain string. If an interface accepts multiple presentations, define a normalization policy first. Do not silently transform all input in ways that destroy meaningful formatting or identifiers.

Read an entire line with getline when spaces belong to the value. Formatted extraction with >> reads a token and can leave the rest of the line. Both operations have useful contracts, but they are not interchangeable. Check the read result before using the text, and distinguish an empty arrived line from end-of-input. The input lesson's delimiter reasoning applies equally when the destination is an owning string rather than a numeric parser.

C++ PRACTICE RUNNER

Build a label and search its contents

The prefix is an owning string, so concatenation uses string operations. find returns the position of the first matching sequence here.

#include <iostream>
#include <string>
int main() {
    const std::string prefix{"Task: "};
    const std::string title{"build"};
    const std::string report{prefix + title};
    const auto position{report.find("build")};
    std::cout << report << '\n';
    if (position != std::string::npos) {
        std::cout << "Found at: " << position << '\n';
        std::cout << "Slice: " << report.substr(position) << '\n';
    }
    return 0;
}
TERMINAL OUTPUT
Predict the output, edit the source, then select Run code.

C++20 compiler runner: source and input are sent only when you press Run to this website's execution endpoint, not an external vendor. Local preview requires Docker and gcc:14; production requires a separately isolated execution service. Network is disabled; execution is limited to 3 seconds, compilation to 10 seconds, memory to 384 MB and output to 32 KB. Single-file programs only. Don't enter secrets. Edits reset on refresh; Copy your work to keep it.

Compare with the starter's expected output
Task: build
Found at: 6
Slice: build

After editing inputs, calculate a new prediction; this reference describes only the starter.

Empty text has a size, but no ordinary first element

For element access, require position < text.size(). Index zero is a meaningful first character only when the string is nonempty. The past-the-end position is not an ordinary character of your data. The standard has particular rules around the terminator at size, but those do not make it a useful element for this lab. Use the simple content-access precondition instead of relying on a terminator detail to process an empty command.

operator[] does not perform the checked bounds behavior that at provides. at throws an out_of_range exception when the requested position is not a valid element. Exceptions are taught later; for now validate the position before access so the operation's precondition is visible. Do not deliberately run an out-of-bounds read as a repair experiment and treat a displayed character as reliable. Repair the condition in a safe example before any access occurs.

Use the string's size-related type for positions when needed, rather than narrowing a possibly large size to int. The lab's ASCII normalization walks char elements using a range-based loop over the string as a short preview of traversal. The full range-based lesson will cover broader iterator and invalidation rules. Do not mutate the string's structure while keeping a pointer or iterator and assuming its earlier validity automatically persists.

C++ PRACTICE RUNNER

Check before reading the first character

An empty command prints Empty. The guard prevents ordinary element access at zero when no content exists.

#include <iostream>
#include <string>
int main() {
    const std::string command;
    if (command.empty()) {
        std::cout << "Empty\n";
    } else {
        std::cout << "First: " << command.at(0) << '\n';
    }
    return 0;
}
TERMINAL OUTPUT
Predict the output, edit the source, then select Run code.

C++20 compiler runner: source and input are sent only when you press Run to this website's execution endpoint, not an external vendor. Local preview requires Docker and gcc:14; production requires a separately isolated execution service. Network is disabled; execution is limited to 3 seconds, compilation to 10 seconds, memory to 384 MB and output to 32 KB. Single-file programs only. Don't enter secrets. Edits reset on refresh; Copy your work to keep it.

Compare with the starter's expected output
Empty

After editing inputs, calculate a new prediction; this reference describes only the starter.

Check npos before turning search into another operation

find returns the first matching position or std::string::npos when the sequence is absent. npos is a sentinel, not a valid index for the located text. Compare with it before calling substr, indexing, or adding an offset. Position zero is a successful find, so testing if (position) would mistakenly treat a match at the start as false while failing to express the absence rule correctly.

substr(position, count) creates an owning copy from a permitted starting position, with count limiting the copied length. A starting position equal to size can produce an empty slice; a starting position beyond size is an error. Distinguish those slice rules from ordinary element indexing, whose valid positions are strictly below size. Check the search result first rather than hoping substr will turn an absent match into the empty text you intended.

A mutation such as append or replace can change length, storage and later positions. A previously found numeric position may still be in range but refer to different text after a change. Keep searches close to their uses or recompute after a relevant mutation. Ownership of the whole string does not make stale positions semantically correct. Tests should describe the relationship you need, such as the word after a colon, rather than merely an index number that happened to work once.

C++ PRACTICE RUNNER

Handle a missing substring explicitly

No match exists. Do not pass npos to substr or interpret it as the last character's position.

#include <iostream>
#include <string>
int main() {
    const std::string text{"build passed"};
    const auto position{text.find("failed")};
    if (position == std::string::npos) {
        std::cout << "Not found\n";
    } else {
        std::cout << text.substr(position) << '\n';
    }
    return 0;
}
TERMINAL OUTPUT
Predict the output, edit the source, then select Run code.

C++20 compiler runner: source and input are sent only when you press Run to this website's execution endpoint, not an external vendor. Local preview requires Docker and gcc:14; production requires a separately isolated execution service. Network is disabled; execution is limited to 3 seconds, compilation to 10 seconds, memory to 384 MB and output to 32 KB. Single-file programs only. Don't enter secrets. Edits reset on refresh; Copy your work to keep it.

Compare with the starter's expected output
Not found

After editing inputs, calculate a new prediction; this reference describes only the starter.

Normalize a small command alphabet—not every language

The lab accepts status or quit, ignoring only leading and trailing ASCII spaces and ASCII letter case. It deliberately does not trim tabs, collapse internal spaces, normalize accented letters, or perform Unicode case folding. A strict policy makes the result testable and avoids changing text under an inflated promise. An input such as sta tus remains unsupported; spaces inside the command are not a cosmetic variation of its spelling.

The reference uses parallel lowercase and uppercase ASCII letter strings to map a known uppercase character to its lowercase counterpart. This avoids claiming an arithmetic character-code trick works on every execution encoding. It also avoids an unexplained call to tolower: that character-classification interface has signed-char and locale considerations. For this limited alphabet, a checked lookup is a small explicit mapping a learner can inspect.

Test lowercase, uppercase, mixed case, surrounding spaces, all spaces, an empty line, internal spaces and a non-ASCII word. Accepted variants should normalize to one of the two documented commands, while unsupported text never becomes an account identifier or a real external action. The program prints a local teaching response only. Preserve the scope of the promise: owning strings and safe searches are demonstrated here, not a production internationalization or security sanitizer.

LOCAL TERMINAL COMMANDS

Compile the examples and your lab

The editors can compile complete single-file programs when the execution service is available. Provide input in the Standard input field before pressing Run. Alternatively save one program as main.cpp and use your installed C++20 compiler with these terminal commands.

macOS / Clang:
clang++ -std=c++20 -Wall -Wextra -Wpedantic main.cpp -o lesson
./lesson

Linux / GCC:
g++ -std=c++20 -Wall -Wextra -Wpedantic main.cpp -o lesson
./lesson

Windows Developer Command Prompt / MSVC:
cl /std:c++20 /EHsc /W4 main.cpp /Fe:lesson.exe
lesson.exe
APPLY THE CONCEPT

Independent lab

Normalize one local ASCII command line to status or quit. Trim only ordinary spaces at its ends and fold only ASCII uppercase letters; reject everything else.

Acceptance checklist

  • Read one complete line and distinguish input exhaustion from an empty command.
  • Trim leading and trailing ASCII spaces, handling an all-space line safely.
  • Map uppercase ASCII letters through a checked lookup.
  • Print Ready for status, Goodbye for quit, otherwise Unsupported with status one.
  • Test mixed case, internal spaces and non-ASCII input without claiming Unicode normalization.
Need a hint?
  1. find_first_not_of and find_last_not_of locate the trim bounds; check npos before slicing.
  2. A range-based char& visits each character of the owned string; structural changes are not needed during this traversal.
  3. Look up a character in the uppercase alphabet and reuse that position in the lowercase alphabet only after checking it.
C++ PRACTICE RUNNER

Write and run your lab

Build your own solution before revealing the reference. Use the input field for normal, boundary and invalid cases; this runner executes your program but does not grade it.

#include <iostream>
#include <string>
#include <sstream>
#include <limits>

int main() {
    // Implement the acceptance checklist above.
    return 0;
}

TERMINAL OUTPUT
Predict the output, edit the source, then select Run code.

C++20 compiler runner: source and input are sent only when you press Run to this website's execution endpoint, not an external vendor. Local preview requires Docker and gcc:14; production requires a separately isolated execution service. Network is disabled; execution is limited to 3 seconds, compilation to 10 seconds, memory to 384 MB and output to 32 KB. Single-file programs only. Don't enter secrets. Edits reset on refresh; Copy your work to keep it.

Your lab evidence and notes

Record predictions, actual output, commands and the cause of each repair. Code is plain text: this scratchpad does not execute or grade C++.

Loading local notes…

Review your acceptance checklist and quiz explanations before marking this lesson complete. This is self-review, not a grade or certification.

Reveal the explained lab solution
C++ PRACTICE RUNNER

Reference lab solution

Compare the reference with your own reasoning. Running this solution does not grade or complete the lesson.

#include <iostream>
#include <string>
std::string normalizeAsciiCommand(std::string text) {
    const auto first{text.find_first_not_of(' ')};
    if (first == std::string::npos) { return {}; }
    const auto last{text.find_last_not_of(' ')};
    text = text.substr(first, last - first + 1);
    const std::string upper{"ABCDEFGHIJKLMNOPQRSTUVWXYZ"};
    const std::string lower{"abcdefghijklmnopqrstuvwxyz"};
    for (char& character : text) {
        const auto position{upper.find(character)};
        if (position != std::string::npos) { character = lower.at(position); }
    }
    return text;
}
int main() {
    std::string line;
    if (!std::getline(std::cin, line)) { std::cout << "Input ended\n"; return 1; }
    const std::string command{normalizeAsciiCommand(line)};
    if (command == "status") { std::cout << "Ready\n"; return 0; }
    if (command == "quit") { std::cout << "Goodbye\n"; return 0; }
    std::cout << "Unsupported\n";
    return 1;
}
TERMINAL OUTPUT
Predict the output, edit the source, then select Run code.

C++20 compiler runner: source and input are sent only when you press Run to this website's execution endpoint, not an external vendor. Local preview requires Docker and gcc:14; production requires a separately isolated execution service. Network is disabled; execution is limited to 3 seconds, compilation to 10 seconds, memory to 384 MB and output to 32 KB. Single-file programs only. Don't enter secrets. Edits reset on refresh; Copy your work to keep it.

Expected output

Ready

The function takes an owning value copy and returns the normalized value, leaving the caller's original line unchanged.

All-space text returns an empty string before substring arithmetic. The alphabet lookup is checked before using its position in the matching lowercase alphabet.

The char& in the range-based loop permits element updates without changing the string's length. Tabs, internal spaces and non-ASCII letters are outside the accepted command policy.

Review before moving on

Explain the lesson's objectives in your own words, compare your lab evidence with the acceptance checklist, and keep the first failing case alongside your repair. Completing a page is not proof that every C++ feature or input is supported.

  • Distinguish an owning std::string from a literal or borrowed view.
  • Read whole lines and compare text contents.
  • Check positions before indexing or slicing.
  • Handle find returning npos before arithmetic.
  • Normalize only a documented ASCII command alphabet.

References and further reading

Original examples and exercises accompany these primary references. Tool-specific diagnostics and support depend on your installed version.

  • C++ draft: string element access
  • C++ draft: string search
  • C++ draft: string substring
KNOWLEDGE CHECK

Check your understanding

Answer all five questions, then review the explanations. Answers are not saved or synced; completion is a separate self-review choice.

01What does std::string own?
02For ASCII status with size six, which is a valid ordinary element position?
03What should happen before using a find result as a located position?
04Does size on a UTF-8 std::string always count visible characters?
05Which transformation does the lab promise?
PREVIOUS LESSONC++ Functions: Parameters and Return Values
NEXT LESSONC++ Vector: std::vector with Examples
ON THIS PAGELearning objectivesA string object owns a sequence of charactersCombine text and compare content deliberatelyEmpty text has a size, but no ordinary first elementCheck npos before turning search into another operationNormalize a small command alphabet—not every languageIndependent labReviewKnowledge checkReferences
Course overview