cpp
examples
examples.cpp⚙️cpp
/**
* Rule of 3/5/0, Move Semantics & RAII - Examples
* Comprehensive demonstration of resource management in C++
*/
#include <iostream>
#include <string>
#include <vector>
#include <memory>
#include <utility>
#include <cstring>
#include <fstream>
#include <mutex>
#include <chrono>
using std::cout;
using std::endl;
using std::string;
// ============================================================================
// SECTION 1: The Problem - Shallow Copy
// ============================================================================
class BadString {
char* data;
size_t len;
public:
BadString(const char* s = "") {
len = strlen(s);
data = new char[len + 1];
strcpy(data, s);
cout << " [BadString] Constructed: \"" << data << "\"" << endl;
}
~BadString() {
cout << " [BadString] Destructor called";
if (data) {
cout << " for: \"" << data << "\"";
delete[] data;
}
cout << endl;
}
// Using default copy - DANGEROUS!
// BadString(const BadString&) = default;
// This does shallow copy: new.data = old.data (same pointer!)
const char* c_str() const { return data; }
};
void demonstrateProblem() {
cout << "\n=== THE PROBLEM: SHALLOW COPY ===" << endl;
cout << "This demonstrates why Rule of 3/5 matters.\n" << endl;
cout << "Creating string a:" << endl;
BadString* a = new BadString("Hello");
cout << "\nCreating string b as copy of a (shallow copy!):" << endl;
// BadString b = *a; // Would cause double-free!
cout << "Both a and b would point to the SAME memory!" << endl;
cout << "When one is destroyed, the other becomes invalid." << endl;
cout << "(Not actually running this to avoid crash)" << endl;
delete a;
}
// ============================================================================
// SECTION 2: Rule of Three
// ============================================================================
class String3 {
char* data;
size_t len;
void copyFrom(const String3& other) {
len = other.len;
data = new char[len + 1];
strcpy(data, other.data);
}
public:
// Constructor
String3(const char* s = "") {
len = strlen(s);
data = new char[len + 1];
strcpy(data, s);
cout << " [String3] Constructed: \"" << data << "\"" << endl;
}
// 1. Destructor
~String3() {
cout << " [String3] Destructor: \"" << data << "\"" << endl;
delete[] data;
}
// 2. Copy Constructor
String3(const String3& other) {
copyFrom(other);
cout << " [String3] Copy constructed: \"" << data << "\"" << endl;
}
// 3. Copy Assignment
String3& operator=(const String3& other) {
cout << " [String3] Copy assignment: \"" << other.data << "\" to \"" << data << "\"" << endl;
if (this != &other) {
delete[] data; // Release old resource
copyFrom(other);
}
return *this;
}
const char* c_str() const { return data; }
size_t length() const { return len; }
};
void demonstrateRuleOfThree() {
cout << "\n=== RULE OF THREE ===" << endl;
cout << "\n1. Construction:" << endl;
String3 a("Hello");
cout << "\n2. Copy construction:" << endl;
String3 b = a; // Copy constructor
cout << "\n3. Copy assignment:" << endl;
String3 c("World");
c = a; // Copy assignment
cout << "\n4. Verify independence (deep copy):" << endl;
cout << "a: " << a.c_str() << endl;
cout << "b: " << b.c_str() << endl;
cout << "c: " << c.c_str() << endl;
cout << "\n5. Destruction (in reverse order):" << endl;
}
// ============================================================================
// SECTION 3: Rule of Five
// ============================================================================
class String5 {
char* data;
size_t len;
public:
// Constructor
String5(const char* s = "") {
len = strlen(s);
data = new char[len + 1];
strcpy(data, s);
cout << " [String5] Constructed: \"" << data << "\"" << endl;
}
// 1. Destructor
~String5() {
cout << " [String5] Destructor";
if (data) {
cout << ": \"" << data << "\"";
delete[] data;
} else {
cout << ": (moved-from)";
}
cout << endl;
}
// 2. Copy Constructor
String5(const String5& other) {
len = other.len;
data = new char[len + 1];
strcpy(data, other.data);
cout << " [String5] Copy constructed: \"" << data << "\"" << endl;
}
// 3. Copy Assignment (using copy-and-swap idiom)
String5& operator=(String5 other) { // Pass by value = copy
cout << " [String5] Copy assignment (swap)" << endl;
swap(*this, other);
return *this;
} // 'other' destroyed here, taking old data with it
// 4. Move Constructor
String5(String5&& other) noexcept
: data(other.data), len(other.len) {
other.data = nullptr;
other.len = 0;
cout << " [String5] Move constructed: \"" << data << "\"" << endl;
}
// 5. Move Assignment
String5& operator=(String5&& other) noexcept {
cout << " [String5] Move assignment" << endl;
if (this != &other) {
delete[] data;
data = other.data;
len = other.len;
other.data = nullptr;
other.len = 0;
}
return *this;
}
// Swap helper
friend void swap(String5& a, String5& b) noexcept {
using std::swap;
swap(a.data, b.data);
swap(a.len, b.len);
}
const char* c_str() const { return data ? data : "(null)"; }
size_t length() const { return len; }
};
void demonstrateRuleOfFive() {
cout << "\n=== RULE OF FIVE ===" << endl;
cout << "\n1. Construction:" << endl;
String5 a("Hello");
cout << "\n2. Copy construction:" << endl;
String5 b = a;
cout << "\n3. Move construction:" << endl;
String5 c = std::move(a);
cout << "After move, a is: \"" << a.c_str() << "\"" << endl;
cout << "\n4. Copy assignment:" << endl;
String5 d("Temp");
d = b; // Copy
cout << "\n5. Move assignment:" << endl;
String5 e("Another");
e = std::move(b);
cout << "After move, b is: \"" << b.c_str() << "\"" << endl;
cout << "\n6. Destruction:" << endl;
}
// ============================================================================
// SECTION 4: Move Semantics in Action
// ============================================================================
class LargeObject {
std::vector<int> data;
static int copyCount;
static int moveCount;
public:
LargeObject(size_t size = 10000) : data(size, 42) {
cout << " [LargeObject] Constructed with " << size << " elements" << endl;
}
LargeObject(const LargeObject& other) : data(other.data) {
++copyCount;
cout << " [LargeObject] COPY (" << copyCount << " total copies)" << endl;
}
LargeObject(LargeObject&& other) noexcept : data(std::move(other.data)) {
++moveCount;
cout << " [LargeObject] MOVE (" << moveCount << " total moves)" << endl;
}
LargeObject& operator=(const LargeObject& other) {
data = other.data;
++copyCount;
cout << " [LargeObject] COPY assignment" << endl;
return *this;
}
LargeObject& operator=(LargeObject&& other) noexcept {
data = std::move(other.data);
++moveCount;
cout << " [LargeObject] MOVE assignment" << endl;
return *this;
}
static void resetCounters() { copyCount = moveCount = 0; }
static void printStats() {
cout << "Copies: " << copyCount << ", Moves: " << moveCount << endl;
}
};
int LargeObject::copyCount = 0;
int LargeObject::moveCount = 0;
LargeObject createObject() {
LargeObject obj(5000);
return obj; // NRVO or move
}
void processObject(LargeObject obj) {
// Process...
}
void demonstrateMoveSemantics() {
cout << "\n=== MOVE SEMANTICS IN ACTION ===" << endl;
cout << "\n1. Vector push_back with copy:" << endl;
LargeObject::resetCounters();
{
std::vector<LargeObject> vec;
LargeObject a;
vec.push_back(a); // Copy
}
LargeObject::printStats();
cout << "\n2. Vector push_back with move:" << endl;
LargeObject::resetCounters();
{
std::vector<LargeObject> vec;
LargeObject a;
vec.push_back(std::move(a)); // Move
}
LargeObject::printStats();
cout << "\n3. Vector emplace_back (construct in place):" << endl;
LargeObject::resetCounters();
{
std::vector<LargeObject> vec;
vec.emplace_back(5000); // Construct directly
}
LargeObject::printStats();
cout << "\n4. Return value (RVO/NRVO):" << endl;
LargeObject::resetCounters();
{
LargeObject x = createObject(); // Should be zero-copy with RVO
}
LargeObject::printStats();
cout << "\n5. Function argument by value:" << endl;
LargeObject::resetCounters();
{
LargeObject a;
processObject(a); // Copy
processObject(std::move(a)); // Move
}
LargeObject::printStats();
}
// ============================================================================
// SECTION 5: Rule of Zero
// ============================================================================
// Class that manages no resources - uses standard library types
class Person {
std::string name;
std::vector<std::string> emails;
std::unique_ptr<std::string> nickname; // Optional nickname
public:
Person(std::string n) : name(std::move(n)) {
cout << " [Person] Constructed: " << name << endl;
}
void addEmail(std::string email) {
emails.push_back(std::move(email));
}
void setNickname(std::string nick) {
nickname = std::make_unique<std::string>(std::move(nick));
}
void print() const {
cout << "Name: " << name << endl;
if (nickname) cout << "Nickname: " << *nickname << endl;
cout << "Emails: ";
for (const auto& e : emails) cout << e << " ";
cout << endl;
}
// Rule of Zero: No destructor, copy/move constructors, or assignments needed!
// The compiler-generated defaults do the right thing.
};
void demonstrateRuleOfZero() {
cout << "\n=== RULE OF ZERO ===" << endl;
cout << "\n1. Create person:" << endl;
Person alice("Alice");
alice.addEmail("alice@example.com");
alice.addEmail("alice@work.com");
alice.setNickname("Ali");
alice.print();
cout << "\n2. Copy person (compiler-generated):" << endl;
Person bob = alice; // Deep copy - works correctly!
bob.print();
cout << "\n3. Move person (compiler-generated):" << endl;
Person charlie = std::move(alice); // Move - works correctly!
charlie.print();
cout << "\nNo manual resource management needed!" << endl;
}
// ============================================================================
// SECTION 6: RAII - File Handle
// ============================================================================
class FileHandle {
FILE* file;
std::string filename;
public:
FileHandle(const std::string& path, const char* mode = "r")
: filename(path) {
file = fopen(path.c_str(), mode);
if (!file) {
throw std::runtime_error("Cannot open file: " + path);
}
cout << " [FileHandle] Opened: " << filename << endl;
}
~FileHandle() {
if (file) {
fclose(file);
cout << " [FileHandle] Closed: " << filename << endl;
}
}
// Non-copyable
FileHandle(const FileHandle&) = delete;
FileHandle& operator=(const FileHandle&) = delete;
// Movable
FileHandle(FileHandle&& other) noexcept
: file(other.file), filename(std::move(other.filename)) {
other.file = nullptr;
cout << " [FileHandle] Moved: " << filename << endl;
}
FileHandle& operator=(FileHandle&& other) noexcept {
if (this != &other) {
if (file) fclose(file);
file = other.file;
filename = std::move(other.filename);
other.file = nullptr;
}
return *this;
}
FILE* get() { return file; }
bool isOpen() const { return file != nullptr; }
};
void demonstrateRAIIFile() {
cout << "\n=== RAII: FILE HANDLE ===" << endl;
try {
cout << "\n1. Normal operation:" << endl;
{
FileHandle file("/tmp/test_raii.txt", "w");
fprintf(file.get(), "Hello RAII!\n");
cout << " Written to file" << endl;
} // File automatically closed here
cout << "\n2. Exception safety:" << endl;
{
FileHandle file("/tmp/test_raii.txt", "r");
// Even if exception thrown here, file is still closed
// throw std::runtime_error("Test exception");
cout << " File would still be closed on exception" << endl;
}
cout << "\n3. Move semantics:" << endl;
{
FileHandle file1("/tmp/test_raii.txt", "r");
FileHandle file2 = std::move(file1);
cout << " file1 moved to file2" << endl;
}
} catch (const std::exception& e) {
cout << "Error: " << e.what() << endl;
}
}
// ============================================================================
// SECTION 7: RAII - Lock Guard
// ============================================================================
class SimpleLockGuard {
std::mutex& mtx;
bool locked;
public:
explicit SimpleLockGuard(std::mutex& m) : mtx(m), locked(true) {
mtx.lock();
cout << " [LockGuard] Acquired lock" << endl;
}
~SimpleLockGuard() {
if (locked) {
mtx.unlock();
cout << " [LockGuard] Released lock" << endl;
}
}
// Non-copyable, non-movable
SimpleLockGuard(const SimpleLockGuard&) = delete;
SimpleLockGuard& operator=(const SimpleLockGuard&) = delete;
SimpleLockGuard(SimpleLockGuard&&) = delete;
SimpleLockGuard& operator=(SimpleLockGuard&&) = delete;
void unlock() {
if (locked) {
mtx.unlock();
locked = false;
cout << " [LockGuard] Manually released" << endl;
}
}
};
void demonstrateRAIILock() {
cout << "\n=== RAII: LOCK GUARD ===" << endl;
std::mutex mtx;
cout << "\n1. Normal scope:" << endl;
{
SimpleLockGuard lock(mtx);
cout << " Doing critical work..." << endl;
} // Lock released
cout << "\n2. Early unlock:" << endl;
{
SimpleLockGuard lock(mtx);
cout << " Critical section 1" << endl;
lock.unlock();
cout << " Non-critical work" << endl;
}
cout << "\n3. Exception safety demo:" << endl;
try {
SimpleLockGuard lock(mtx);
cout << " About to throw..." << endl;
// throw std::runtime_error("Test");
cout << " (exception would still release lock)" << endl;
} catch (...) {
cout << " Exception caught, lock was released" << endl;
}
}
// ============================================================================
// SECTION 8: RAII - Scoped Timer
// ============================================================================
class ScopedTimer {
std::chrono::high_resolution_clock::time_point start;
std::string name;
public:
ScopedTimer(std::string n) : name(std::move(n)) {
start = std::chrono::high_resolution_clock::now();
}
~ScopedTimer() {
auto end = std::chrono::high_resolution_clock::now();
auto duration = std::chrono::duration<double, std::milli>(end - start);
cout << " [Timer] " << name << ": " << duration.count() << " ms" << endl;
}
ScopedTimer(const ScopedTimer&) = delete;
ScopedTimer& operator=(const ScopedTimer&) = delete;
};
void demonstrateRAIITimer() {
cout << "\n=== RAII: SCOPED TIMER ===" << endl;
{
ScopedTimer timer("Vector fill");
std::vector<int> v;
for (int i = 0; i < 100000; ++i) {
v.push_back(i);
}
}
{
ScopedTimer timer("String concatenation");
std::string s;
for (int i = 0; i < 10000; ++i) {
s += "x";
}
}
{
ScopedTimer timer("Reserve + fill");
std::vector<int> v;
v.reserve(100000);
for (int i = 0; i < 100000; ++i) {
v.push_back(i);
}
}
}
// ============================================================================
// SECTION 9: Smart Pointers as RAII
// ============================================================================
class Resource {
int id;
public:
Resource(int i) : id(i) {
cout << " [Resource] Created #" << id << endl;
}
~Resource() {
cout << " [Resource] Destroyed #" << id << endl;
}
void use() const {
cout << " [Resource] Using #" << id << endl;
}
};
void demonstrateSmartPointersRAII() {
cout << "\n=== SMART POINTERS AS RAII ===" << endl;
cout << "\n1. unique_ptr:" << endl;
{
auto ptr = std::make_unique<Resource>(1);
ptr->use();
} // Automatically deleted
cout << "\n2. shared_ptr:" << endl;
{
std::shared_ptr<Resource> ptr1 = std::make_shared<Resource>(2);
{
std::shared_ptr<Resource> ptr2 = ptr1; // Shared ownership
cout << " Reference count: " << ptr1.use_count() << endl;
}
cout << " Reference count: " << ptr1.use_count() << endl;
} // Deleted when last shared_ptr goes out of scope
cout << "\n3. unique_ptr with custom deleter:" << endl;
{
auto fileDeleter = [](FILE* f) {
if (f) {
fclose(f);
cout << " [CustomDeleter] File closed" << endl;
}
};
std::unique_ptr<FILE, decltype(fileDeleter)>
file(fopen("/tmp/test.txt", "w"), fileDeleter);
if (file) {
fprintf(file.get(), "Hello custom deleter!\n");
}
} // Custom deleter called
}
// ============================================================================
// SECTION 10: Practical Example - Database Connection
// ============================================================================
class DatabaseConnection {
std::string connectionString;
bool connected;
int queryCount;
public:
explicit DatabaseConnection(std::string connStr)
: connectionString(std::move(connStr)), connected(false), queryCount(0) {
// Simulate connection
cout << " [DB] Connecting to: " << connectionString << endl;
connected = true;
cout << " [DB] Connected!" << endl;
}
~DatabaseConnection() {
if (connected) {
cout << " [DB] Disconnecting... (" << queryCount << " queries executed)" << endl;
connected = false;
}
}
// Non-copyable (database connection shouldn't be copied)
DatabaseConnection(const DatabaseConnection&) = delete;
DatabaseConnection& operator=(const DatabaseConnection&) = delete;
// Movable
DatabaseConnection(DatabaseConnection&& other) noexcept
: connectionString(std::move(other.connectionString)),
connected(other.connected),
queryCount(other.queryCount) {
other.connected = false;
cout << " [DB] Connection moved" << endl;
}
DatabaseConnection& operator=(DatabaseConnection&& other) noexcept {
if (this != &other) {
if (connected) {
cout << " [DB] Closing old connection" << endl;
}
connectionString = std::move(other.connectionString);
connected = other.connected;
queryCount = other.queryCount;
other.connected = false;
}
return *this;
}
void query(const std::string& sql) {
if (!connected) throw std::runtime_error("Not connected!");
cout << " [DB] Executing: " << sql << endl;
++queryCount;
}
bool isConnected() const { return connected; }
};
void demonstrateDatabaseRAII() {
cout << "\n=== PRACTICAL EXAMPLE: DATABASE CONNECTION ===" << endl;
cout << "\n1. Normal usage:" << endl;
{
DatabaseConnection db("postgresql://localhost/mydb");
db.query("SELECT * FROM users");
db.query("UPDATE users SET active = true");
} // Connection closed automatically
cout << "\n2. Exception safety:" << endl;
try {
DatabaseConnection db("mysql://localhost/test");
db.query("SELECT * FROM products");
// Simulate error
// throw std::runtime_error("Query failed!");
db.query("DELETE FROM products WHERE id = 5");
} catch (const std::exception& e) {
cout << " Error: " << e.what() << endl;
}
// Connection still closed properly!
cout << "\n3. Transfer ownership:" << endl;
{
auto createConnection = []() {
return DatabaseConnection("sqlite://data.db");
};
DatabaseConnection conn = createConnection(); // Move construction
conn.query("CREATE TABLE IF NOT EXISTS logs");
}
}
// ============================================================================
// MAIN
// ============================================================================
int main() {
cout << "╔══════════════════════════════════════════════════════════════╗" << endl;
cout << "║ RULE OF 3/5/0, MOVE SEMANTICS & RAII EXAMPLES ║" << endl;
cout << "╚══════════════════════════════════════════════════════════════╝" << endl;
demonstrateProblem();
demonstrateRuleOfThree();
demonstrateRuleOfFive();
demonstrateMoveSemantics();
demonstrateRuleOfZero();
demonstrateRAIIFile();
demonstrateRAIILock();
demonstrateRAIITimer();
demonstrateSmartPointersRAII();
demonstrateDatabaseRAII();
cout << "\n═══════════════════════════════════════════════════════════════" << endl;
cout << "All examples completed!" << endl;
return 0;
}