C++ for C Developers | Lesson 2: Beyond printf: C++ I/O Streams & std::string
Goal: Learn how to read input using std::cin
, understand the benefits of C++ stream I/O over printf
/scanf
, and get introduced to the powerful std::string
class for handling text.
1. Reading Input with std::cin
Just as std::cout
handles output, std::cin
(from <iostream>
) handles standard input (usually the keyboard). We use the stream extraction operator (>>
) to get data from the stream.
#include <iostream>
#include <string> // Need this header for std::string
int main() {
int age;
double weight;
std::string name; // Using the C++ string class! (More on this soon)
// Prompt the user for their age
std::cout << "Please enter your age: ";
std::cin >> age; // Read an integer from input into the 'age' variable
// Prompt the user for their weight
std::cout << "Please enter your weight (e.g., 75.5): ";
std::cin >> weight; // Read a double from input into 'weight'
// Prompt the user for their first name
std::cout << "Please enter your first name: ";
std::cin >> name; // Read a sequence of non-whitespace characters into 'name'
// Display the collected information
std::cout << "\n--- You entered ---\n";
std::cout << "Name: " << name << std::endl;
std::cout << "Age: " << age << std::endl;
std::cout << "Weight: " << weight << std::endl;
return 0;
}
How cin >> ...
Works:
- It skips leading whitespace (spaces, tabs, newlines).
- It reads characters from the input buffer until it encounters whitespace or input that doesn’t match the type of the variable it’s trying to read into.
- It attempts to convert the read characters into the type of the variable (e.g.,
int
,double
,std::string
). - Like
cout <<
, you can chaincin >>
:std::cin >> var1 >> var2;
flowchart TD A[User types input] --> B[Input Buffer receives characters] B --> C{Is there leading whitespace?} C -- Yes --> D[Skip whitespace] C -- No --> E[Read input based on variable type] E --> F{Successful conversion?} F -- Yes --> G[Assign value to variable] F -- No --> H[Enter error state]
2. cin
/cout
vs. scanf
/printf
Why use this over the familiar scanf
and printf
?
Feature | printf /scanf (C-style) | cout /cin (C++ Streams) |
---|---|---|
Type Safety | No. Requires correct format specifiers (%d , %f , %s ). Mismatch leads to undefined behavior. | Yes. Type is determined by the variable. Safer. |
Extensibility | Difficult to add support for custom types. | Easier. Can overload << and >> for user-defined types (classes). |
Syntax | Format string + variable list. Error-prone. | Uses operators (<< , >> ). Often cleaner for basic types. |
Header | <cstdio> (or <stdio.h> ) | <iostream> |
Example of Type Safety Issue in C:
int x;
printf("Enter a number: ");
scanf("%f", &x); // WRONG specifier! Trying to read float into int address. Undefined Behavior!
C++ avoids this:
int x;
std::cout << "Enter a number: ";
std::cin >> x; // Reads an integer. If user types "abc", cin enters an error state.
// If user types "12.5", it likely reads 12 and leaves ".5" in the buffer.
classDiagram class CStyle_IO { - scanf() - printf() + Format Strings - Type Unsafe - Less Extensible } class CppStyle_IO { - std::cin - std::cout + Stream Operators (<<, >>) + Type Safe + Custom Type Overload } CStyle_IO <|-- InputOutput CppStyle_IO <|-- InputOutput
3. The Problem with cin >> string
and Whitespace
Notice in the first example, we asked for the first name. What happens if you enter “John Doe”?
std::cin >> name;
will only read “John” into the name
variable. The ” Doe” part will be left in the input buffer, potentially causing issues for subsequent cin
calls.
4. Reading Entire Lines: std::getline
To read a whole line of text, including spaces, until the user presses Enter, use the std::getline
function (defined in the <string>
header).
#include <iostream>
#include <string> // Need this header!
int main() {
std::string fullName;
int id;
std::cout << "Enter your ID: ";
std::cin >> id;
// *** Important: Consume the leftover newline character ***
// After 'cin >> id;', the user pressed Enter, leaving '\n' in the buffer.
// std::getline reads until '\n', so we need to get rid of the one left by cin.
std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n'); // Clears buffer up to newline
std::cout << "Enter your full name: ";
std::getline(std::cin, fullName); // Read the entire line into fullName
std::cout << "\n--- Entered Data ---" << std::endl;
std::cout << "ID: " << id << std::endl;
std::cout << "Full Name: '" << fullName << "'" << std::endl; // Quotes show extent
return 0;
}
(Note: std::numeric_limits
requires #include <limits>
)
Key Takeaway: Be careful when mixing cin >> ...
and std::getline
. The cin >> ...
often leaves the newline character (\n
) in the input buffer, which getline
then immediately reads as an empty line. Use cin.ignore(...)
to discard the leftover newline before calling getline
.
5. Introducing std::string
In C, you work with char
arrays and pointers (char*
) for strings. This involves manual memory management (using malloc
/free
for dynamic strings), careful bounds checking to avoid buffer overflows, and using functions from <string.h>
(like strcpy
, strcat
, strlen
).
C++ offers the std::string
class (from <string>
) which makes string manipulation much easier and safer.
graph LR A["C-style string (char*)"] B["std::string"] A --> A1["Manual memory allocation (malloc / free)"] A --> A2["Functions from string.h (e.g., strcpy, strlen)"] A --> A3["Risk of buffer overflows"] B --> B1["Automatic memory management"] B --> B2["Rich member functions (length, find, replace)"] B --> B3["Safer, bounds-checked access"] B --> B4["Seamless integration with streams"]
Benefits of std::string
:
- Automatic Memory Management: It handles its own memory. No need for
malloc
/free
for the string data itself. It grows automatically as needed. - Rich Functionality: Provides many built-in member functions for common operations (finding substrings, replacing characters, getting length, etc.).
- Safer: Reduces risks of buffer overflows compared to fixed-size
char
arrays or improper use ofstrcpy
/strcat
. - Integration with Streams: Works seamlessly with
cout <<
andcin >>
(andgetline
).
Basic std::string
Usage:
#include <iostream>
#include <string> // Don't forget this!
int main() {
// Declaration and Initialization
std::string s1; // Default constructor: empty string ""
std::string s2 = "Hello"; // Initialize from string literal
std::string s3(" World"); // Initialize with constructor syntax
std::string s4(s2); // Copy constructor: s4 is now "Hello"
std::string s5(5, 'x'); // Creates "xxxxx" (5 copies of 'x')
// Concatenation (joining strings)
std::string message = s2 + s3 + "!"; // Uses the '+' operator -> "Hello World!"
message += " How are you?"; // Append using '+=' -> "Hello World! How are you?"
// Getting Length
std::cout << "Message: " << message << std::endl;
std::cout << "Length: " << message.length() << std::endl; // or message.size()
// Comparison
std::string name1 = "Alice";
std::string name2 = "Bob";
if (name1 == "Alice") {
std::cout << "name1 is Alice" << std::endl;
}
if (name1 < name2) { // Lexicographical comparison (like strcmp)
std::cout << name1 << " comes before " << name2 << std::endl;
}
// Accessing Characters (like arrays, but safer ways exist)
// char firstChar = message[0]; // 'H' - similar to C arrays
// std::cout << "First char: " << firstChar << std::endl;
// Note: Direct index access doesn't do bounds checking by default.
// message.at(index) is a safer alternative that throws an error if out of bounds.
return 0;
}
Summary of Lesson 2:
- Use
std::cin >> variable;
to read formatted input. - C++ stream I/O is generally type-safer than
scanf
/printf
. - The extraction operator
>>
stops at whitespace. - Use
std::getline(std::cin, string_variable);
to read an entire line including spaces. - Remember to handle the leftover newline when mixing
cin >>
andgetline
. - Include the
<string>
header to usestd::string
. std::string
provides automatic memory management, easy concatenation (+
), length (.length()
), comparison (==
,<
,>
), and many other useful operations, making it far superior to C-style strings for most tasks.
Next Lesson: We’ll explore enhancements C++ brings to functions, including function overloading and default arguments, allowing for more flexible and readable function definitions.
More Sources:
cppreference – std::string: https://en.cppreference.com/w/cpp/string/basic_string
Learn C++ – std::string Tutorial: https://www.learncpp.com/cpp-tutorial/stdstring/