cpp

examples

examples.cpp⚙️
/**
 * Smart Pointers in C++ - Comprehensive Examples
 * 
 * Demonstrates:
 * - unique_ptr usage
 * - shared_ptr usage
 * - weak_ptr usage
 * - Custom deleters
 * - Ownership patterns
 * 
 * Compile: g++ -std=c++17 -Wall -Wextra examples.cpp -o examples
 * Run: ./examples
 */

#include <iostream>
#include <memory>
#include <string>
#include <vector>

using namespace std;

// Helper class to track construction/destruction
class Widget {
    static int nextId;
    int id;
    string name;
    
public:
    Widget(const string& n = "Unnamed") : id(nextId++), name(n) {
        cout << "  Widget " << id << " (" << name << ") constructed" << endl;
    }
    
    ~Widget() {
        cout << "  Widget " << id << " (" << name << ") destroyed" << endl;
    }
    
    void doSomething() const {
        cout << "  Widget " << id << " (" << name << ") doing something" << endl;
    }
    
    int getId() const { return id; }
    string getName() const { return name; }
};

int Widget::nextId = 1;

// ============================================================
// SECTION 1: UNIQUE_PTR BASICS
// ============================================================

void demonstrateUniquePtr() {
    cout << "--- unique_ptr Basics ---\n" << endl;
    
    // Create unique_ptr using make_unique (preferred)
    cout << "Creating with make_unique:" << endl;
    unique_ptr<Widget> p1 = make_unique<Widget>("First");
    p1->doSomething();
    
    // Create unique_ptr directly (less preferred)
    cout << "\nCreating directly:" << endl;
    unique_ptr<Widget> p2(new Widget("Second"));
    p2->doSomething();
    
    // Access raw pointer
    cout << "\nAccessing raw pointer:" << endl;
    Widget* raw = p1.get();
    cout << "  Raw pointer: " << raw << endl;
    cout << "  ID via raw: " << raw->getId() << endl;
    
    // Check validity
    cout << "\nCheck validity:" << endl;
    if (p1) {
        cout << "  p1 is valid" << endl;
    }
    
    // Reset
    cout << "\nResetting p2:" << endl;
    p2.reset();  // Deletes the Widget
    if (!p2) {
        cout << "  p2 is now nullptr" << endl;
    }
    
    cout << "\nLeaving scope (p1 will be destroyed):" << endl;
}

void demonstrateUniquePtrMove() {
    cout << "\n--- unique_ptr Move Semantics ---\n" << endl;
    
    unique_ptr<Widget> owner = make_unique<Widget>("Moveable");
    cout << "Original owner has Widget" << endl;
    
    // Cannot copy!
    // unique_ptr<Widget> copy = owner;  // ERROR!
    
    // Can move
    unique_ptr<Widget> newOwner = move(owner);
    cout << "\nAfter move:" << endl;
    cout << "  Original owner: " << (owner ? "has Widget" : "nullptr") << endl;
    cout << "  New owner: " << (newOwner ? "has Widget" : "nullptr") << endl;
    
    if (newOwner) {
        newOwner->doSomething();
    }
    
    cout << "\nLeaving scope:" << endl;
}

void demonstrateUniquePtrArray() {
    cout << "\n--- unique_ptr with Arrays ---\n" << endl;
    
    // Array specialization
    unique_ptr<int[]> arr = make_unique<int[]>(5);
    
    // Fill array
    for (int i = 0; i < 5; i++) {
        arr[i] = (i + 1) * 10;
    }
    
    // Print array
    cout << "Array contents: ";
    for (int i = 0; i < 5; i++) {
        cout << arr[i] << " ";
    }
    cout << endl;
    
    // Automatically calls delete[]
}

// ============================================================
// SECTION 2: UNIQUE_PTR OWNERSHIP PATTERNS
// ============================================================

// Factory function returning unique_ptr
unique_ptr<Widget> createWidget(const string& name) {
    cout << "Factory creating Widget:" << endl;
    return make_unique<Widget>(name);
}

// Function taking ownership
void takeOwnership(unique_ptr<Widget> w) {
    cout << "takeOwnership received Widget" << endl;
    w->doSomething();
    cout << "takeOwnership ending (Widget will be destroyed):" << endl;
}

// Function borrowing (not owning)
void borrowWidget(Widget& w) {
    cout << "borrowWidget (non-owning reference):" << endl;
    w.doSomething();
}

void demonstrateOwnership() {
    cout << "\n--- Ownership Patterns ---\n" << endl;
    
    // Factory returns ownership
    cout << "Using factory:" << endl;
    unique_ptr<Widget> w1 = createWidget("Factory Widget");
    w1->doSomething();
    
    // Transfer ownership to function
    cout << "\nTransferring ownership:" << endl;
    unique_ptr<Widget> w2 = make_unique<Widget>("Transfer Me");
    takeOwnership(move(w2));
    cout << "After transfer, w2 is: " << (w2 ? "valid" : "nullptr") << endl;
    
    // Borrow without ownership
    cout << "\nBorrowing (non-owning):" << endl;
    unique_ptr<Widget> w3 = make_unique<Widget>("Borrowable");
    borrowWidget(*w3);  // Widget still owned by w3
    cout << "w3 still owns the Widget" << endl;
    
    cout << "\nLeaving scope:" << endl;
}

// ============================================================
// SECTION 3: SHARED_PTR BASICS
// ============================================================

void demonstrateSharedPtr() {
    cout << "\n--- shared_ptr Basics ---\n" << endl;
    
    // Create shared_ptr using make_shared (preferred)
    cout << "Creating shared_ptr:" << endl;
    shared_ptr<Widget> sp1 = make_shared<Widget>("Shared");
    cout << "Reference count: " << sp1.use_count() << endl;
    
    // Copy creates another owner
    cout << "\nCopying (shared ownership):" << endl;
    shared_ptr<Widget> sp2 = sp1;
    shared_ptr<Widget> sp3 = sp1;
    cout << "Reference count: " << sp1.use_count() << endl;
    
    // All pointers point to the same Widget
    cout << "\nAll pointers access the same Widget:" << endl;
    cout << "  sp1 ID: " << sp1->getId() << endl;
    cout << "  sp2 ID: " << sp2->getId() << endl;
    cout << "  sp3 ID: " << sp3->getId() << endl;
    
    // Reset one pointer
    cout << "\nResetting sp2:" << endl;
    sp2.reset();
    cout << "Reference count: " << sp1.use_count() << endl;
    
    // Reset another
    cout << "\nResetting sp3:" << endl;
    sp3.reset();
    cout << "Reference count: " << sp1.use_count() << endl;
    
    cout << "\nLeaving scope (sp1 destroyed, Widget deleted):" << endl;
}

void demonstrateSharedPtrScope() {
    cout << "\n--- shared_ptr Scope Behavior ---\n" << endl;
    
    shared_ptr<Widget> outer = make_shared<Widget>("Scoped");
    cout << "Outer scope - count: " << outer.use_count() << endl;
    
    {
        cout << "\nEntering inner scope:" << endl;
        shared_ptr<Widget> inner1 = outer;
        cout << "  After inner1 = outer, count: " << outer.use_count() << endl;
        
        {
            shared_ptr<Widget> inner2 = outer;
            cout << "  After inner2 = outer, count: " << outer.use_count() << endl;
        }
        cout << "  Left innermost scope, count: " << outer.use_count() << endl;
    }
    cout << "Left inner scope, count: " << outer.use_count() << endl;
    
    cout << "\nLeaving outer scope:" << endl;
}

// ============================================================
// SECTION 4: WEAK_PTR
// ============================================================

void demonstrateWeakPtr() {
    cout << "\n--- weak_ptr Basics ---\n" << endl;
    
    weak_ptr<Widget> wp;
    
    {
        shared_ptr<Widget> sp = make_shared<Widget>("Temporary");
        cout << "shared_ptr count: " << sp.use_count() << endl;
        
        wp = sp;  // Create weak_ptr from shared_ptr
        cout << "After creating weak_ptr, count: " << sp.use_count() << endl;
        cout << "weak_ptr expired? " << (wp.expired() ? "yes" : "no") << endl;
        
        // Lock to use
        cout << "\nUsing lock() to access:" << endl;
        if (shared_ptr<Widget> locked = wp.lock()) {
            locked->doSomething();
            cout << "  Count during lock: " << locked.use_count() << endl;
        }
        
        cout << "\nLeaving scope (shared_ptr destroyed):" << endl;
    }
    
    cout << "\nAfter shared_ptr destroyed:" << endl;
    cout << "weak_ptr expired? " << (wp.expired() ? "yes" : "no") << endl;
    
    if (shared_ptr<Widget> locked = wp.lock()) {
        cout << "This won't print" << endl;
    } else {
        cout << "Cannot lock - object no longer exists" << endl;
    }
}

void demonstrateCircularReference() {
    cout << "\n--- Breaking Circular References ---\n" << endl;
    
    // Using shared_ptr for both directions would cause a leak
    // Using weak_ptr for back-pointer breaks the cycle
    
    struct Node {
        string name;
        shared_ptr<Node> next;
        weak_ptr<Node> prev;  // Weak to break cycle
        
        Node(const string& n) : name(n) {
            cout << "  Node " << name << " created" << endl;
        }
        ~Node() {
            cout << "  Node " << name << " destroyed" << endl;
        }
    };
    
    cout << "Creating doubly-linked nodes:" << endl;
    {
        shared_ptr<Node> first = make_shared<Node>("First");
        shared_ptr<Node> second = make_shared<Node>("Second");
        
        first->next = second;
        second->prev = first;  // Weak reference back
        
        cout << "\nAccessing prev via weak_ptr:" << endl;
        if (auto prev = second->prev.lock()) {
            cout << "  Second's prev is: " << prev->name << endl;
        }
        
        cout << "\nLeaving scope:" << endl;
    }
    cout << "Both nodes properly destroyed!" << endl;
}

// ============================================================
// SECTION 5: CUSTOM DELETERS
// ============================================================

void demonstrateCustomDeleters() {
    cout << "\n--- Custom Deleters ---\n" << endl;
    
    // Lambda deleter with unique_ptr
    cout << "unique_ptr with lambda deleter:" << endl;
    {
        auto customDeleter = [](int* p) {
            cout << "  Custom deleter called for value: " << *p << endl;
            delete p;
        };
        
        unique_ptr<int, decltype(customDeleter)> ptr(new int(42), customDeleter);
        cout << "  Value: " << *ptr << endl;
        cout << "  Leaving scope:" << endl;
    }
    
    // Lambda deleter with shared_ptr (easier syntax)
    cout << "\nshared_ptr with lambda deleter:" << endl;
    {
        shared_ptr<int> ptr(new int(100), [](int* p) {
            cout << "  Custom deleter for shared_ptr: " << *p << endl;
            delete p;
        });
        cout << "  Value: " << *ptr << endl;
        cout << "  Leaving scope:" << endl;
    }
    
    // Array deleter
    cout << "\nshared_ptr with array deleter:" << endl;
    {
        shared_ptr<int> arr(new int[5], default_delete<int[]>());
        cout << "  Array created" << endl;
    }
    cout << "  Array properly deleted" << endl;
}

// ============================================================
// SECTION 6: PRACTICAL EXAMPLES
// ============================================================

// Simple object pool using shared_ptr
class WidgetPool {
    vector<shared_ptr<Widget>> pool;
    
public:
    shared_ptr<Widget> getWidget(const string& name) {
        // Could implement recycling logic here
        auto w = make_shared<Widget>(name);
        pool.push_back(w);
        return w;
    }
    
    void showStats() {
        cout << "Pool has " << pool.size() << " widgets" << endl;
        for (const auto& w : pool) {
            cout << "  " << w->getName() << " (refs: " << w.use_count() << ")" << endl;
        }
    }
};

void demonstratePool() {
    cout << "\n--- Widget Pool Example ---\n" << endl;
    
    WidgetPool pool;
    
    shared_ptr<Widget> w1 = pool.getWidget("PoolWidget1");
    shared_ptr<Widget> w2 = pool.getWidget("PoolWidget2");
    
    // Create additional references
    shared_ptr<Widget> w1_copy = w1;
    
    cout << "\nPool stats:" << endl;
    pool.showStats();
    
    cout << "\nLeaving scope:" << endl;
}

// Observer pattern with weak_ptr
class Observer {
    string name;
    
public:
    Observer(const string& n) : name(n) {
        cout << "  Observer " << name << " created" << endl;
    }
    
    ~Observer() {
        cout << "  Observer " << name << " destroyed" << endl;
    }
    
    void onNotify(const string& message) {
        cout << "  " << name << " received: " << message << endl;
    }
};

class Subject {
    vector<weak_ptr<Observer>> observers;
    
public:
    void addObserver(shared_ptr<Observer> obs) {
        observers.push_back(obs);
    }
    
    void notify(const string& message) {
        cout << "Subject notifying observers..." << endl;
        
        for (auto& wp : observers) {
            if (auto sp = wp.lock()) {
                sp->onNotify(message);
            } else {
                cout << "  (Observer no longer exists)" << endl;
            }
        }
    }
};

void demonstrateObserver() {
    cout << "\n--- Observer Pattern with weak_ptr ---\n" << endl;
    
    Subject subject;
    
    {
        auto obs1 = make_shared<Observer>("Observer1");
        auto obs2 = make_shared<Observer>("Observer2");
        
        subject.addObserver(obs1);
        subject.addObserver(obs2);
        
        subject.notify("First notification");
        
        cout << "\nDestroying Observer1:" << endl;
        obs1.reset();
        
        subject.notify("Second notification");
        
        cout << "\nLeaving scope:" << endl;
    }
    
    subject.notify("Third notification (no observers left)");
}

// ============================================================
// SECTION 7: COMPARISON
// ============================================================

void demonstrateComparison() {
    cout << "\n--- Smart Pointer Comparison ---\n" << endl;
    
    cout << "unique_ptr:" << endl;
    cout << "  - Exclusive ownership" << endl;
    cout << "  - No overhead (same as raw pointer)" << endl;
    cout << "  - Move-only (cannot copy)" << endl;
    cout << "  - Use: Single owner, most cases" << endl;
    
    cout << "\nshared_ptr:" << endl;
    cout << "  - Shared ownership" << endl;
    cout << "  - Overhead: Reference count (atomic operations)" << endl;
    cout << "  - Can copy" << endl;
    cout << "  - Use: Multiple owners needed" << endl;
    
    cout << "\nweak_ptr:" << endl;
    cout << "  - Non-owning observer" << endl;
    cout << "  - Must lock before use" << endl;
    cout << "  - Use: Breaking cycles, caching, observers" << endl;
    
    // Size comparison
    cout << "\nSizes:" << endl;
    cout << "  sizeof(int*):           " << sizeof(int*) << " bytes" << endl;
    cout << "  sizeof(unique_ptr<int>): " << sizeof(unique_ptr<int>) << " bytes" << endl;
    cout << "  sizeof(shared_ptr<int>): " << sizeof(shared_ptr<int>) << " bytes" << endl;
    cout << "  sizeof(weak_ptr<int>):   " << sizeof(weak_ptr<int>) << " bytes" << endl;
}

// ============================================================
// MAIN FUNCTION
// ============================================================

int main() {
    cout << "╔════════════════════════════════════════════════════════════════════╗" << endl;
    cout << "║                   C++ SMART POINTERS - EXAMPLES                    ║" << endl;
    cout << "║                unique_ptr, shared_ptr, weak_ptr                    ║" << endl;
    cout << "╚════════════════════════════════════════════════════════════════════╝" << endl;
    
    cout << "\n╔══════════════════════════════════════════════════════════════╗" << endl;
    cout << "║              SECTION 1: unique_ptr Basics                    ║" << endl;
    cout << "╚══════════════════════════════════════════════════════════════╝" << endl;
    demonstrateUniquePtr();
    demonstrateUniquePtrMove();
    demonstrateUniquePtrArray();
    
    cout << "\n╔══════════════════════════════════════════════════════════════╗" << endl;
    cout << "║              SECTION 2: Ownership Patterns                   ║" << endl;
    cout << "╚══════════════════════════════════════════════════════════════╝" << endl;
    demonstrateOwnership();
    
    cout << "\n╔══════════════════════════════════════════════════════════════╗" << endl;
    cout << "║              SECTION 3: shared_ptr                           ║" << endl;
    cout << "╚══════════════════════════════════════════════════════════════╝" << endl;
    demonstrateSharedPtr();
    demonstrateSharedPtrScope();
    
    cout << "\n╔══════════════════════════════════════════════════════════════╗" << endl;
    cout << "║              SECTION 4: weak_ptr                             ║" << endl;
    cout << "╚══════════════════════════════════════════════════════════════╝" << endl;
    demonstrateWeakPtr();
    demonstrateCircularReference();
    
    cout << "\n╔══════════════════════════════════════════════════════════════╗" << endl;
    cout << "║              SECTION 5: Custom Deleters                      ║" << endl;
    cout << "╚══════════════════════════════════════════════════════════════╝" << endl;
    demonstrateCustomDeleters();
    
    cout << "\n╔══════════════════════════════════════════════════════════════╗" << endl;
    cout << "║              SECTION 6: Practical Examples                   ║" << endl;
    cout << "╚══════════════════════════════════════════════════════════════╝" << endl;
    demonstratePool();
    demonstrateObserver();
    
    cout << "\n╔══════════════════════════════════════════════════════════════╗" << endl;
    cout << "║              SECTION 7: Comparison                           ║" << endl;
    cout << "╚══════════════════════════════════════════════════════════════╝" << endl;
    demonstrateComparison();
    
    cout << "\n╔════════════════════════════════════════════════════════════════════╗" << endl;
    cout << "║                       Examples Complete!                           ║" << endl;
    cout << "╚════════════════════════════════════════════════════════════════════╝" << endl;
    
    return 0;
}
Examples - C++ Tutorial | DeepML