System Programming Topic
Modern C++ Features
1. Modern C++ Features Overview
auto - Automatic type deduction (C++11)
cpp
#include <iostream>
#include <vector>
#include <map>
#include <string>
using namespace std;
int main() {
// AUTO: Compiler deduces type automatically
auto x = 10; // int
auto y = 3.14; // double
auto z = "Hello"; // const char*
auto flag = true; // bool
cout << "x type: " << typeid(x).name() << ", value: " << x << endl;
cout << "y type: " << typeid(y).name() << ", value: " << y << endl;
// Complex types become readable
vector<pair<string, int>> data = {{"Alice", 25}, {"Bob", 30}};
// Without auto - verbose
vector<pair<string, int>>::iterator it1 = data.begin();
// With auto - clean
auto it2 = data.begin();
// Iterating containers
cout << "\nUsing auto with iterators:" << endl;
for (auto it = data.begin(); it != data.end(); ++it) {
cout << it->first << ": " << it->second << endl;
}
// auto in function return types (C++14)
auto add = [](auto a, auto b) { return a + b; };
cout << "\nAdd with auto: 5 + 3.14 = " << add(5, 3.14) << endl;
// decltype(auto) - preserves references and const-ness (C++14)
const int& cref = x;
decltype(auto) d1 = cref; // d1 is const int&
return 0;
}
nullptr - Type-safe null pointer (C++11)
cpp
#include <iostream>
using namespace std;
void func(int* ptr) {
if (ptr == nullptr) {
cout << "Received null pointer" << endl;
} else {
cout << "Pointer points to: " << *ptr << endl;
}
}
void func(int num) {
cout << "Received integer: " << num << endl;
}
int main() {
// NULLPTR: Type-safe null pointer (replaces NULL/0)
int* ptr1 = nullptr; // Good - type safe
int* ptr2 = NULL; // Old style (usually 0)
int* ptr3 = 0; // Problematic - ambiguous
int value = 5;
int* ptr = &value;
// Check for null
if (ptr != nullptr) {
cout << "Pointer is not null" << endl;
}
// Function overloading example
func(nullptr); // Calls func(int*) - unambiguous
// func(NULL); // Ambiguous in some cases (calls func(int))
func(0); // Calls func(int)
// Use nullptr in templates
auto nullPtr = nullptr; // Type is std::nullptr_t
return 0;
}
Range-based for loops - Clean iteration over containers (C++11)
cpp
#include <iostream>
#include <vector>
#include <map>
#include <string>
#include <array>
using namespace std;
int main() {
// RANGE-BASED FOR LOOPS: Clean syntax for container iteration
vector<int> numbers = {1, 2, 3, 4, 5};
// Traditional loop
cout << "Traditional for loop:" << endl;
for (size_t i = 0; i < numbers.size(); ++i) {
cout << numbers[i] << " ";
}
cout << endl;
// Range-based for loop (by value - copies)
cout << "\nRange-based for loop (by value):" << endl;
for (int num : numbers) {
cout << num << " ";
num *= 2; // Doesn't modify original
}
cout << endl;
// Range-based for loop (by reference - modifies original)
cout << "Range-based for loop (by reference):" << endl;
for (int& num : numbers) {
num *= 2; // Modifies original
cout << num << " ";
}
cout << endl;
// Range-based for loop (const reference - read-only)
cout << "Range-based for loop (const reference):" << endl;
for (const int& num : numbers) {
cout << num << " ";
// num *= 2; // Error: can't modify const reference
}
cout << endl;
// Works with arrays
int arr[] = {10, 20, 30, 40, 50};
cout << "\nArray iteration:" << endl;
for (int val : arr) {
cout << val << " ";
}
cout << endl;
// Works with initializer lists
cout << "Initializer list:" << endl;
for (int val : {100, 200, 300}) {
cout << val << " ";
}
cout << endl;
// Works with maps
map<string, int> scores = {{"Alice", 95}, {"Bob", 87}, {"Charlie", 92}};
cout << "\nMap iteration:" << endl;
for (const auto& pair : scores) {
cout << pair.first << ": " << pair.second << endl;
}
// Structured bindings with range-based for (C++17)
cout << "\nWith structured bindings (C++17):" << endl;
for (const auto& [name, score] : scores) {
cout << name << ": " << score << endl;
}
return 0;
}
constexpr - Compile-time evaluation (C++11/14/17)
cpp
#include <iostream>
#include <array>
using namespace std;
// CONSTEXPR: Indicates value/function can be evaluated at compile time
// Constexpr function (C++11)
constexpr int square(int x) {
return x * x;
}
// Constexpr function with if (C++14)
constexpr int factorial(int n) {
if (n <= 1) return 1;
return n * factorial(n - 1);
}
// Constexpr with loops (C++14)
constexpr int sumUpTo(int n) {
int sum = 0;
for (int i = 1; i <= n; ++i) {
sum += i;
}
return sum;
}
// Constexpr variable
constexpr double PI = 3.141592653589793;
// Constexpr class
class Circle {
private:
double radius;
public:
// Constexpr constructor
constexpr Circle(double r) : radius(r) {}
// Constexpr member function
constexpr double getArea() const {
return PI * radius * radius;
}
constexpr double getRadius() const { return radius; }
};
int main() {
// Compile-time constants
constexpr int size = 10;
constexpr int squared = square(5); // Computed at compile time
constexpr int fact = factorial(5); // Computed at compile time
constexpr int sum = sumUpTo(10); // Computed at compile time
cout << "Square of 5: " << squared << endl;
cout << "Factorial of 5: " << fact << endl;
cout << "Sum up to 10: " << sum << endl;
// Using constexpr with arrays (size must be compile-time constant)
array<int, square(3)> arr; // Array size = 9
// Constexpr objects
constexpr Circle smallCircle(2.0);
constexpr double area = smallCircle.getArea(); // Computed at compile time
cout << "\nCircle with radius " << smallCircle.getRadius()
<< " has area " << area << endl;
// Runtime vs compile-time
int input;
cout << "\nEnter a number: ";
cin >> input;
int runtimeSquare = square(input); // Computed at runtime
cout << "Square of " << input << " (runtime): " << runtimeSquare << endl;
// const vs constexpr
const int constValue = 10; // Can be runtime or compile-time
constexpr int constexprValue = 10; // Must be compile-time
// Array example
constexpr int arraySize = 5;
int compileTimeArray[arraySize]; // OK
int userSize;
cout << "Enter array size: ";
cin >> userSize;
const int runtimeSize = userSize;
// int runtimeArray[runtimeSize]; // Error in standard C++ (variable length array)
return 0;
}
enum class - Type-safe enumerations (C++11)
cpp
#include <iostream>
#include <string>
using namespace std;
// ENUM CLASS: Strongly typed, scoped enumerations
// Traditional enum (unscoped, implicit conversion to int)
enum Color { RED, GREEN, BLUE }; // RED is in global scope
// enum class (scoped, no implicit conversion)
enum class TrafficLight { RED, YELLOW, GREEN };
// enum class with underlying type
enum class StatusCode : int { OK = 200, NOT_FOUND = 404, ERROR = 500 };
// enum class with string conversion
enum class LogLevel { DEBUG, INFO, WARNING, ERROR };
string logLevelToString(LogLevel level) {
switch (level) {
case LogLevel::DEBUG: return "DEBUG";
case LogLevel::INFO: return "INFO";
case LogLevel::WARNING: return "WARNING";
case LogLevel::ERROR: return "ERROR";
default: return "UNKNOWN";
}
}
int main() {
// Traditional enum issues
Color color1 = RED; // OK
Color color2 = GREEN; // OK
// int num = RED; // Implicit conversion (not type-safe)
// Problems with traditional enums
enum Fruit { APPLE, BANANA, ORANGE };
// Color myColor = APPLE; // Error: name conflict (both have value 0)
// enum class - type safe
TrafficLight light = TrafficLight::RED;
// TrafficLight light2 = RED; // Error: must use TrafficLight::RED
// No implicit conversion
// int lightValue = light; // Error: no implicit conversion
int lightValue = static_cast<int>(light); // Explicit conversion
cout << "Traffic light value: " << lightValue << endl;
// Using switch with enum class
switch (light) {
case TrafficLight::RED:
cout << "Stop!" << endl;
break;
case TrafficLight::YELLOW:
cout << "Slow down!" << endl;
break;
case TrafficLight::GREEN:
cout << "Go!" << endl;
break;
}
// enum class with underlying type
StatusCode code = StatusCode::OK;
int httpCode = static_cast<int>(code);
cout << "\nHTTP Status Code: " << httpCode << endl;
// Compare enum values
if (code == StatusCode::OK) {
cout << "Request successful!" << endl;
}
// Log level example
LogLevel currentLevel = LogLevel::INFO;
cout << "\nCurrent log level: " << logLevelToString(currentLevel) << endl;
// Iterating over enum values (requires workaround)
cout << "\nAll log levels:" << endl;
for (int i = 0; i <= static_cast<int>(LogLevel::ERROR); ++i) {
LogLevel level = static_cast<LogLevel>(i);
cout << logLevelToString(level) << endl;
}
// Benefits of enum class:
// 1. No name collisions (scoped)
// 2. No implicit conversions (type-safe)
// 3. Can specify underlying type
// 4. Can use with overloaded operators
return 0;
}
2. Lambda Expressions
Lambda Expressions - Anonymous functions (C++11+)
cpp
#include <iostream>
#include <vector>
#include <algorithm>
#include <functional>
using namespace std;
int main() {
// LAMBDA EXPRESSIONS: Anonymous function objects
// Basic syntax: [capture](parameters) -> return_type { body }
// 1. Basic lambda
auto greet = []() {
cout << "Hello from lambda!" << endl;
};
greet();
// 2. Lambda with parameters and return type
auto add = [](int a, int b) -> int {
return a + b;
};
cout << "5 + 3 = " << add(5, 3) << endl;
// 3. Lambda with auto parameters (C++14)
auto multiply = [](auto a, auto b) {
return a * b;
};
cout << "5 * 3.14 = " << multiply(5, 3.14) << endl;
// 4. Immediately invoked lambda
[]() {
cout << "Immediately invoked!" << endl;
}();
int result = [](int x, int y) { return x * y; }(4, 5);
cout << "4 * 5 = " << result << endl;
// CAPTURE LISTS: How lambda accesses external variables
int x = 10;
int y = 20;
// 5. Capture by value [=]
auto lambda1 = [=]() {
cout << "Capture by value: x=" << x << ", y=" << y << endl;
// x = 30; // Error: can't modify captured by value
};
lambda1();
// 6. Capture by reference [&]
auto lambda2 = [&]() {
cout << "Capture by reference: x=" << x << ", y=" << y << endl;
x = 30; // OK: modifies original
y = 40;
};
lambda2();
cout << "After lambda2: x=" << x << ", y=" << y << endl;
// 7. Mixed capture [x, &y]
x = 10; y = 20;
auto lambda3 = [x, &y]() {
cout << "Mixed capture: x=" << x << ", y=" << y << endl;
// x = 50; // Error: captured by value
y = 60; // OK: captured by reference
};
lambda3();
cout << "After lambda3: x=" << x << ", y=" << y << endl;
// 8. Capture with initialization (C++14)
auto lambda4 = [z = x + y]() {
cout << "Capture with init: z=" << z << endl;
};
lambda4();
// 9. Mutable lambdas (allows modifying captured by value)
auto lambda5 = [x]() mutable {
x = 100; // OK: modifies local copy
cout << "Mutable lambda: x=" << x << endl;
};
lambda5();
cout << "Original x unchanged: " << x << endl;
// PRACTICAL USE CASES
vector<int> numbers = {5, 2, 8, 1, 9, 3, 7, 4, 6};
// 10. Use with algorithms
cout << "\nOriginal numbers: ";
for_each(numbers.begin(), numbers.end(), [](int n) { cout << n << " "; });
cout << endl;
// Sort with custom comparator
sort(numbers.begin(), numbers.end(), [](int a, int b) {
return a > b; // Sort descending
});
cout << "Sorted descending: ";
for (int n : numbers) cout << n << " ";
cout << endl;
// Count if
int countEven = count_if(numbers.begin(), numbers.end(), [](int n) {
return n % 2 == 0;
});
cout << "Even numbers count: " << countEven << endl;
// Transform
vector<int> squares;
squares.resize(numbers.size());
transform(numbers.begin(), numbers.end(), squares.begin(),
[](int n) { return n * n; });
cout << "Squares: ";
for (int n : squares) cout << n << " ";
cout << endl;
// Find if
auto it = find_if(numbers.begin(), numbers.end(), [](int n) {
return n > 5;
});
if (it != numbers.end()) {
cout << "First number > 5: " << *it << endl;
}
// 11. Function composition
auto increment = [](int n) { return n + 1; };
auto squareFn = [](int n) { return n * n; };
// Compose functions
auto incrementThenSquare = [increment, squareFn](int n) {
return squareFn(increment(n));
};
cout << "Increment then square of 4: " << incrementThenSquare(4) << endl;
// 12. Callback functions
class Button {
public:
using ClickHandler = function<void()>;
void setOnClick(ClickHandler handler) {
onClick = handler;
}
void click() {
if (onClick) onClick();
}
private:
ClickHandler onClick;
};
Button btn;
string userName = "Alice";
btn.setOnClick([userName]() {
cout << "Button clicked by " << userName << "!" << endl;
});
btn.click();
// 13. Generic lambda with auto (C++14)
auto printPair = [](const auto& p) {
cout << "Pair: " << p.first << ", " << p.second << endl;
};
printPair(make_pair(10, "Hello"));
printPair(make_pair(3.14, true));
// 14. Lambda as return type
auto makeMultiplier = [](int factor) {
return [factor](int n) { return n * factor; };
};
auto doubleIt = makeMultiplier(2);
auto tripleIt = makeMultiplier(3);
cout << "Double 5: " << doubleIt(5) << endl;
cout << "Triple 5: " << tripleIt(5) << endl;
return 0;
}
3. Concurrency & Multithreading
std::thread - Basic threading (C++11)
cpp
#include <iostream>
#include <thread>
#include <chrono>
#include <vector>
#include <mutex>
#include <future>
#include <atomic>
using namespace std;
// MUTEX: Mutual exclusion for thread safety
mutex coutMutex; // Global mutex for safe console output
void printSafe(const string& message) {
lock_guard<mutex> lock(coutMutex);
cout << message << endl;
}
void basicThreadExample() {
cout << "\n=== BASIC THREAD EXAMPLE ===" << endl;
// Creating threads
auto worker = [](int id) {
this_thread::sleep_for(chrono::milliseconds(100 * id));
printSafe("Thread " + to_string(id) + " finished");
};
thread t1(worker, 1);
thread t2(worker, 2);
thread t3(worker, 3);
// Main thread continues execution
// Wait for threads to finish
t1.join();
t2.join();
t3.join();
cout << "All threads completed" << endl;
}
// RACE CONDITION: Unsynchronized access to shared data
void raceConditionExample() {
cout << "\n=== RACE CONDITION EXAMPLE ===" << endl;
int sharedCounter = 0;
const int iterations = 100000;
auto incrementer = [&sharedCounter, iterations]() {
for (int i = 0; i < iterations; ++i) {
sharedCounter++; // Not thread-safe!
}
};
thread t1(incrementer);
thread t2(incrementer);
t1.join();
t2.join();
// Expected: 200000, but we get less due to race condition
cout << "Counter value (expected " << 2 * iterations << "): "
<< sharedCounter << endl;
}
// Solving race condition with MUTEX
void mutexExample() {
cout << "\n=== MUTEX EXAMPLE ===" << endl;
int sharedCounter = 0;
const int iterations = 100000;
mutex counterMutex;
auto safeIncrementer = [&sharedCounter, iterations, &counterMutex]() {
for (int i = 0; i < iterations; ++i) {
lock_guard<mutex> lock(counterMutex); // RAII lock
sharedCounter++; // Thread-safe
}
};
thread t1(safeIncrementer);
thread t2(safeIncrementer);
t1.join();
t2.join();
cout << "Counter value (correct): " << sharedCounter << endl;
}
// ATOMIC operations (lock-free)
void atomicExample() {
cout << "\n=== ATOMIC EXAMPLE ===" << endl;
atomic<int> atomicCounter(0); // Atomic variable
const int iterations = 100000;
auto atomicIncrementer = [&atomicCounter, iterations]() {
for (int i = 0; i < iterations; ++i) {
atomicCounter++; // Atomic operation
}
};
thread t1(atomicIncrementer);
thread t2(atomicIncrementer);
t1.join();
t2.join();
cout << "Atomic counter value: " << atomicCounter.load() << endl;
}
// std::async - Higher-level abstraction for async tasks
void asyncExample() {
cout << "\n=== ASYNC EXAMPLE ===" << endl;
// Function that simulates long computation
auto compute = [](int x) -> int {
this_thread::sleep_for(chrono::milliseconds(1000));
return x * x;
};
// Launch async tasks
auto start = chrono::high_resolution_clock::now();
future<int> future1 = async(launch::async, compute, 5);
future<int> future2 = async(launch::async, compute, 10);
// Do other work while tasks run...
// Get results (blocks if not ready)
int result1 = future1.get();
int result2 = future2.get();
auto end = chrono::high_resolution_clock::now();
auto duration = chrono::duration_cast<chrono::milliseconds>(end - start);
cout << "Result 1: " << result1 << endl;
cout << "Result 2: " << result2 << endl;
cout << "Total time (parallel): " << duration.count() << "ms" << endl;
// Compare with sequential execution
start = chrono::high_resolution_clock::now();
int seq1 = compute(5);
int seq2 = compute(10);
end = chrono::high_resolution_clock::now();
duration = chrono::duration_cast<chrono::milliseconds>(end - start);
cout << "Sequential time: " << duration.count() << "ms" << endl;
}
// Producer-Consumer pattern
void producerConsumerExample() {
cout << "\n=== PRODUCER-CONSUMER EXAMPLE ===" << endl;
vector<int> buffer;
const int maxSize = 5;
mutex bufferMutex;
condition_variable bufferNotEmpty;
condition_variable bufferNotFull;
atomic<bool> done(false);
auto producer = [&](int id) {
for (int i = 0; i < 10; ++i) {
unique_lock<mutex> lock(bufferMutex);
// Wait if buffer is full
bufferNotFull.wait(lock, [&]() {
return buffer.size() < maxSize;
});
// Produce item
int item = id * 100 + i;
buffer.push_back(item);
printSafe("Producer " + to_string(id) + " produced: " + to_string(item));
lock.unlock();
bufferNotEmpty.notify_one();
this_thread::sleep_for(chrono::milliseconds(100));
}
};
auto consumer = [&](int id) {
while (!done || !buffer.empty()) {
unique_lock<mutex> lock(bufferMutex);
// Wait if buffer is empty and not done
bufferNotEmpty.wait_for(lock, chrono::milliseconds(100),
[&]() { return !buffer.empty() || done; });
if (!buffer.empty()) {
// Consume item
int item = buffer.back();
buffer.pop_back();
printSafe("Consumer " + to_string(id) + " consumed: " + to_string(item));
lock.unlock();
bufferNotFull.notify_one();
}
}
};
// Start producers and consumers
vector<thread> producers;
vector<thread> consumers;
for (int i = 0; i < 2; ++i) {
producers.emplace_back(producer, i);
consumers.emplace_back(consumer, i);
}
// Let producers finish
for (auto& t : producers) t.join();
done = true;
// Let consumers finish
for (auto& t : consumers) t.join();
cout << "Producer-consumer completed" << endl;
}
int main() {
cout << "=== MODERN C++ CONCURRENCY ===" << endl;
basicThreadExample();
raceConditionExample();
mutexExample();
atomicExample();
asyncExample();
producerConsumerExample();
cout << "\nAll concurrency examples completed!" << endl;
return 0;
}
4. File Handling
File Streams - Reading and writing files
cpp
#include <iostream>
#include <fstream>
#include <string>
#include <vector>
#include <iomanip>
#include <sstream>
using namespace std;
void textFileExample() {
cout << "=== TEXT FILE HANDLING ===" << endl;
// WRITING TO A TEXT FILE
// ofstream: Output file stream
ofstream outFile("example.txt");
if (!outFile) {
cerr << "Error creating file!" << endl;
return;
}
// Write data
outFile << "Hello, File Handling!" << endl;
outFile << "This is line 2." << endl;
outFile << "Line 3 with number: " << 42 << endl;
outFile << "Pi: " << 3.14159 << endl;
// Write formatted output
outFile << left << setw(15) << "Name"
<< setw(10) << "Age"
<< setw(10) << "Score" << endl;
outFile << string(35, '-') << endl;
outFile << left << setw(15) << "Alice"
<< setw(10) << 25
<< setw(10) << fixed << setprecision(2) << 95.5 << endl;
outFile << left << setw(15) << "Bob"
<< setw(10) << 30
<< setw(10) << 87.25 << endl;
outFile.close();
cout << "Data written to example.txt" << endl;
// READING FROM A TEXT FILE
// ifstream: Input file stream
ifstream inFile("example.txt");
if (!inFile) {
cerr << "Error opening file!" << endl;
return;
}
cout << "\nReading file line by line:" << endl;
string line;
while (getline(inFile, line)) {
cout << line << endl;
}
inFile.close();
// READING WITH DIFFERENT TECHNIQUES
cout << "\nReading word by word:" << endl;
inFile.open("example.txt");
string word;
while (inFile >> word) {
cout << word << " ";
}
cout << endl;
inFile.close();
// READING SPECIFIC DATA
cout << "\nReading structured data:" << endl;
inFile.open("example.txt");
// Skip header lines
for (int i = 0; i < 5; ++i) getline(inFile, line);
// Read table data
string name;
int age;
double score;
while (inFile >> name >> age >> score) {
cout << "Name: " << name
<< ", Age: " << age
<< ", Score: " << score << endl;
}
inFile.close();
}
void binaryFileExample() {
cout << "\n=== BINARY FILE HANDLING ===" << endl;
struct Person {
char name[50];
int age;
double salary;
};
vector<Person> people = {
{"Alice Johnson", 25, 55000.50},
{"Bob Smith", 30, 65000.75},
{"Charlie Brown", 35, 75000.25}
};
// WRITING BINARY DATA
ofstream outFile("people.bin", ios::binary);
if (!outFile) {
cerr << "Error creating binary file!" << endl;
return;
}
// Write each person
for (const auto& person : people) {
outFile.write(reinterpret_cast<const char*>(&person), sizeof(Person));
}
outFile.close();
cout << "Binary data written to people.bin" << endl;
// READING BINARY DATA
ifstream inFile("people.bin", ios::binary);
if (!inFile) {
cerr << "Error opening binary file!" << endl;
return;
}
cout << "\nReading binary data:" << endl;
Person person;
while (inFile.read(reinterpret_cast<char*>(&person), sizeof(Person))) {
cout << "Name: " << person.name
<< ", Age: " << person.age
<< ", Salary: " << person.salary << endl;
}
inFile.close();
// APPENDING TO BINARY FILE
fstream appFile("people.bin", ios::binary | ios::in | ios::out | ios::app);
if (!appFile) {
cerr << "Error opening file for appending!" << endl;
return;
}
Person newPerson = {"David Wilson", 28, 60000.00};
appFile.write(reinterpret_cast<const char*>(&newPerson), sizeof(Person));
// Go to beginning and read all
appFile.seekg(0, ios::beg);
cout << "\nAfter appending:" << endl;
while (appFile.read(reinterpret_cast<char*>(&person), sizeof(Person))) {
cout << "Name: " << person.name
<< ", Age: " << person.age
<< ", Salary: " << person.salary << endl;
}
appFile.close();
}
void fileManipulationExample() {
cout << "\n=== FILE MANIPULATION ===" << endl;
// CHECKING FILE PROPERTIES
ifstream testFile("example.txt");
if (testFile.is_open()) {
cout << "File is open" << endl;
// Get file size
testFile.seekg(0, ios::end);
streampos fileSize = testFile.tellg();
cout << "File size: " << fileSize << " bytes" << endl;
testFile.seekg(0, ios::beg); // Reset to beginning
// Get current position
streampos pos = testFile.tellg();
cout << "Current position: " << pos << endl;
testFile.close();
}
// FILE STATES
cout << "\nFile states:" << endl;
ifstream stateFile("nonexistent.txt");
if (!stateFile) {
cout << "File not found or couldn't open" << endl;
}
if (stateFile.fail()) {
cout << "failbit set (I/O operation failed)" << endl;
}
if (stateFile.bad()) {
cout << "badbit set (unrecoverable error)" << endl;
}
if (stateFile.eof()) {
cout << "eofbit set (end of file reached)" << endl;
}
stateFile.close();
// FILE COPY EXAMPLE
cout << "\nCopying file..." << endl;
ifstream source("example.txt", ios::binary);
ofstream destination("example_copy.txt", ios::binary);
if (!source || !destination) {
cerr << "Error in file copy!" << endl;
return;
}
// Efficient file copy
destination << source.rdbuf();
source.close();
destination.close();
cout << "File copied successfully!" << endl;
}
void csvFileExample() {
cout << "\n=== CSV FILE HANDLING ===" << endl;
// WRITING CSV
ofstream csvFile("data.csv");
csvFile << "Name,Age,City,Salary" << endl;
csvFile << "Alice,25,New York,55000.50" << endl;
csvFile << "Bob,30,Los Angeles,65000.75" << endl;
csvFile << "Charlie,35,Chicago,75000.25" << endl;
csvFile.close();
// READING CSV
ifstream inCsv("data.csv");
string csvLine;
cout << "\nReading CSV file:" << endl;
// Skip header
getline(inCsv, csvLine);
while (getline(inCsv, csvLine)) {
stringstream ss(csvLine);
string name, city;
int age;
double salary;
// Parse CSV line
getline(ss, name, ',');
ss >> age;
ss.ignore(); // Ignore comma
getline(ss, city, ',');
ss >> salary;
cout << "Name: " << name
<< ", Age: " << age
<< ", City: " << city
<< ", Salary: " << salary << endl;
}
inCsv.close();
}
void practicalFileExample() {
cout << "\n=== PRACTICAL FILE EXAMPLE ===" << endl;
// Simple database using files
class StudentDatabase {
private:
string filename;
struct Student {
int id;
string name;
double gpa;
string toString() const {
return to_string(id) + "," + name + "," + to_string(gpa);
}
static Student fromString(const string& str) {
stringstream ss(str);
string token;
Student s;
getline(ss, token, ',');
s.id = stoi(token);
getline(ss, s.name, ',');
getline(ss, token, ',');
s.gpa = stod(token);
return s;
}
};
public:
StudentDatabase(const string& fname) : filename(fname) {}
void addStudent(int id, const string& name, double gpa) {
ofstream file(filename, ios::app);
if (file) {
file << Student{id, name, gpa}.toString() << endl;
}
}
vector<Student> getAllStudents() {
vector<Student> students;
ifstream file(filename);
string line;
while (getline(file, line)) {
students.push_back(Student::fromString(line));
}
return students;
}
Student findStudent(int id) {
ifstream file(filename);
string line;
while (getline(file, line)) {
Student s = Student::fromString(line);
if (s.id == id) {
return s;
}
}
return Student{0, "Not Found", 0.0};
}
};
// Usage
StudentDatabase db("students.csv");
// Add students
db.addStudent(101, "Alice Johnson", 3.8);
db.addStudent(102, "Bob Smith", 3.5);
db.addStudent(103, "Charlie Brown", 3.9);
// Retrieve and display
auto students = db.getAllStudents();
cout << "\nAll Students:" << endl;
cout << left << setw(10) << "ID"
<< setw(20) << "Name"
<< setw(10) << "GPA" << endl;
cout << string(40, '-') << endl;
for (const auto& student : students) {
cout << setw(10) << student.id
<< setw(20) << student.name
<< setw(10) << fixed << setprecision(2) << student.gpa << endl;
}
// Search
Student found = db.findStudent(102);
cout << "\nFound student 102: " << found.name
<< " (GPA: " << found.gpa << ")" << endl;
}
int main() {
cout << "=== MODERN C++ FILE HANDLING ===" << endl;
textFileExample();
binaryFileExample();
fileManipulationExample();
csvFileExample();
practicalFileExample();
cout << "\nAll file handling examples completed!" << endl;
// Clean up created files
remove("example.txt");
remove("example_copy.txt");
remove("people.bin");
remove("students.csv");
remove("data.csv");
return 0;
}
Key Takeaways:
Modern C++ Features:
-
auto: Type deduction - cleaner code, especially with complex types
-
nullptr: Type-safe null pointer - eliminates ambiguity with NULL/0
-
Range-based loops: Clean iteration over containers -
for (auto& item : container) -
constexpr: Compile-time evaluation - performance optimization
-
enum class: Type-safe enums - prevents name collisions, no implicit conversions
Lambda Expressions:
-
Syntax:
[capture](params) -> return_type { body } -
Capture lists:
[=](by value),[&](by ref),[x, &y](mixed) -
Use cases: Callbacks, STL algorithms, async operations
-
Mutable lambdas: Allow modifying captured-by-value variables
Concurrency & Multithreading:
-
std::thread: Basic thread management
-
Mutex/Locks: Prevent race conditions (
lock_guard,unique_lock) -
Condition variables: Thread synchronization
-
std::async: High-level async task execution
-
Atomic operations: Lock-free thread-safe operations
File Handling:
-
Text files:
ifstream/ofstreamwith>>/<<operators -
Binary files: Use
read()/write()withios::binary -
File states:
good(),fail(),bad(),eof() -
Manipulation:
seekg(),tellg(), file copying -
Practical patterns: CSV parsing, simple databases, logging
Comments (0)
No comments yet. Be the first to comment!