cpp

examples

examples.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;
}
Examples - C++ Tutorial | DeepML