cpp

exercises

exercises.cpp⚙️
/**
 * Smart Pointers in C++ - Exercises
 * 
 * Practice problems for mastering smart pointers.
 * Each exercise includes TODO sections to complete.
 * 
 * Compile: g++ -std=c++17 -Wall -Wextra exercises.cpp -o exercises
 * Run: ./exercises
 */

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

using namespace std;

// Helper class for exercises
class Resource {
    string name;
    static int count;
    
public:
    Resource(const string& n = "Unnamed") : name(n) {
        count++;
        cout << "  Resource '" << name << "' created (total: " << count << ")" << endl;
    }
    
    ~Resource() {
        count--;
        cout << "  Resource '" << name << "' destroyed (remaining: " << count << ")" << endl;
    }
    
    void use() const {
        cout << "  Using resource: " << name << endl;
    }
    
    string getName() const { return name; }
    static int getCount() { return count; }
};

int Resource::count = 0;

// ============================================================
// Exercise 1: Create unique_ptr ⭐
// ============================================================
/**
 * Create a unique_ptr using make_unique.
 */
void exercise1() {
    // TODO: Create a unique_ptr<Resource> named 'res' with name "Exercise1"
    // Hint: use make_unique
    
    // TODO: Call res->use()
    
    cout << "(Resource should be destroyed when leaving scope)" << endl;
}

// ============================================================
// Exercise 2: Transfer unique_ptr Ownership ⭐⭐
// ============================================================
/**
 * Transfer ownership of unique_ptr between variables.
 */
void exercise2() {
    unique_ptr<Resource> original = make_unique<Resource>("Original");
    
    // TODO: Transfer ownership from 'original' to 'transferred'
    unique_ptr<Resource> transferred;  // Fix this line
    
    // Check the transfer
    cout << "original is: " << (original ? "valid" : "nullptr") << endl;
    cout << "transferred is: " << (transferred ? "valid" : "nullptr") << endl;
    
    if (transferred) {
        transferred->use();
    }
}

// ============================================================
// Exercise 3: Factory Function ⭐⭐
// ============================================================
/**
 * Create a factory function that returns unique_ptr.
 */
unique_ptr<Resource> createResource(const string& name) {
    // TODO: Create and return a unique_ptr<Resource>
    return nullptr;  // Fix this
}

void exercise3() {
    unique_ptr<Resource> res = createResource("FactoryMade");
    
    if (res) {
        res->use();
        cout << "Factory created: " << res->getName() << endl;
    }
}

// ============================================================
// Exercise 4: unique_ptr Array ⭐⭐
// ============================================================
/**
 * Use unique_ptr with arrays.
 */
void exercise4() {
    // TODO: Create a unique_ptr to an array of 5 integers
    // Hint: unique_ptr<int[]> arr = make_unique<int[]>(5);
    
    // TODO: Fill with values 10, 20, 30, 40, 50
    
    cout << "Array: ";
    // TODO: Print array elements
    cout << "(expected: 10 20 30 40 50)" << endl;
}

// ============================================================
// Exercise 5: shared_ptr Basics ⭐⭐
// ============================================================
/**
 * Demonstrate shared_ptr reference counting.
 */
void exercise5() {
    // TODO: Create a shared_ptr<Resource> named 'sp1' with name "Shared"
    shared_ptr<Resource> sp1;  // Fix this
    
    cout << "After creation, count: " << sp1.use_count() << " (expected: 1)" << endl;
    
    // TODO: Create sp2 as a copy of sp1
    shared_ptr<Resource> sp2;  // Fix this
    
    cout << "After copy, count: " << sp1.use_count() << " (expected: 2)" << endl;
    
    // TODO: Create sp3 as another copy
    shared_ptr<Resource> sp3;  // Fix this
    
    cout << "After 2nd copy, count: " << sp1.use_count() << " (expected: 3)" << endl;
    
    // TODO: Reset sp2
    
    cout << "After reset sp2, count: " << sp1.use_count() << " (expected: 2)" << endl;
}

// ============================================================
// Exercise 6: weak_ptr Usage ⭐⭐⭐
// ============================================================
/**
 * Use weak_ptr to observe without owning.
 */
void exercise6() {
    weak_ptr<Resource> wp;
    
    cout << "Before scope, weak_ptr expired: " << wp.expired() << " (expected: 1/true)" << endl;
    
    {
        shared_ptr<Resource> sp = make_shared<Resource>("Observable");
        
        // TODO: Assign sp to wp
        
        cout << "Inside scope, expired: " << wp.expired() << " (expected: 0/false)" << endl;
        cout << "Reference count: " << sp.use_count() << " (expected: 1, weak doesn't count)" << endl;
        
        // TODO: Lock the weak_ptr and use the resource
        // Hint: if (auto locked = wp.lock()) { ... }
        
    }  // sp destroyed here
    
    cout << "After scope, expired: " << wp.expired() << " (expected: 1/true)" << endl;
    
    // TODO: Try to lock again and handle the failure
    
}

// ============================================================
// Exercise 7: Break Circular Reference ⭐⭐⭐
// ============================================================
/**
 * Use weak_ptr to break a circular reference.
 */
struct Node {
    string name;
    shared_ptr<Node> next;
    // TODO: Change this to weak_ptr to break the cycle
    shared_ptr<Node> prev;  // This causes a memory leak!
    
    Node(const string& n) : name(n) {
        cout << "  Node '" << name << "' created" << endl;
    }
    ~Node() {
        cout << "  Node '" << name << "' destroyed" << endl;
    }
};

void exercise7() {
    cout << "Creating nodes:" << endl;
    {
        shared_ptr<Node> n1 = make_shared<Node>("Node1");
        shared_ptr<Node> n2 = make_shared<Node>("Node2");
        
        n1->next = n2;
        n2->prev = n1;  // Creates circular reference!
        
        cout << "Leaving scope (both nodes should be destroyed):" << endl;
    }
    cout << "(If using shared_ptr for prev, nodes WON'T be destroyed - leak!)" << endl;
    cout << "(Change prev to weak_ptr to fix)" << endl;
}

// ============================================================
// Exercise 8: Pass unique_ptr to Function ⭐⭐
// ============================================================
/**
 * Demonstrate different ways to pass smart pointers to functions.
 */

// Takes ownership
void takeResource(unique_ptr<Resource> res) {
    cout << "  takeResource received: ";
    if (res) {
        res->use();
    }
    cout << "  (Resource destroyed at function end)" << endl;
}

// Borrows (doesn't take ownership)
void useResource(Resource& res) {
    cout << "  useResource borrowed: ";
    res.use();
}

void exercise8() {
    unique_ptr<Resource> res = make_unique<Resource>("Passable");
    
    // TODO: Borrow the resource (pass reference)
    cout << "Borrowing:" << endl;
    // useResource(...);
    
    cout << "After borrow, res is: " << (res ? "valid" : "nullptr") << endl;
    
    // TODO: Transfer ownership to function
    cout << "Transferring:" << endl;
    // takeResource(...);
    
    cout << "After transfer, res is: " << (res ? "valid" : "nullptr") << endl;
}

// ============================================================
// Exercise 9: Custom Deleter ⭐⭐⭐
// ============================================================
/**
 * Create a smart pointer with a custom deleter.
 */
void exercise9() {
    cout << "Creating with custom deleter:" << endl;
    
    // TODO: Create a shared_ptr<int> with a custom deleter that prints a message
    // Hint: shared_ptr<int> ptr(new int(42), [](int* p) { ... delete p; });
    
    shared_ptr<int> ptr;  // Fix this
    
    if (ptr) {
        cout << "Value: " << *ptr << endl;
    }
    
    cout << "Leaving scope (custom deleter should print):" << endl;
}

// ============================================================
// Exercise 10: Container of shared_ptr ⭐⭐
// ============================================================
/**
 * Store shared_ptrs in a vector.
 */
void exercise10() {
    vector<shared_ptr<Resource>> resources;
    
    // TODO: Add 3 resources to the vector
    // Hint: resources.push_back(make_shared<Resource>("Name"));
    
    cout << "\nResources in vector:" << endl;
    for (const auto& res : resources) {
        res->use();
        cout << "  Reference count: " << res.use_count() << endl;
    }
    
    // TODO: Create an external reference to the first resource
    // shared_ptr<Resource> external = resources[0];
    
    cout << "\nFirst resource count after external ref: " 
         << 0 << " (expected: 2)" << endl;  // Fix this
    
    cout << "\nClearing vector:" << endl;
    resources.clear();
    
    // external still keeps the first resource alive (if you created it)
}

// ============================================================
// Exercise 11: Observer Pattern ⭐⭐⭐
// ============================================================
/**
 * Implement a simple observer using 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 notify(const string& msg) {
        cout << "  " << name << " received: " << msg << endl;
    }
};

class Subject {
    // TODO: Store weak_ptrs to observers
    vector<weak_ptr<Observer>> observers;
    
public:
    void addObserver(shared_ptr<Observer> obs) {
        // TODO: Add observer to the list
        
    }
    
    void notifyAll(const string& message) {
        // TODO: Iterate through observers, lock each, and notify if valid
        
    }
};

void exercise11() {
    Subject subject;
    
    {
        auto obs1 = make_shared<Observer>("Obs1");
        auto obs2 = make_shared<Observer>("Obs2");
        
        subject.addObserver(obs1);
        subject.addObserver(obs2);
        
        cout << "Notifying (both observers exist):" << endl;
        subject.notifyAll("Hello observers!");
        
        cout << "\nDestroying obs1:" << endl;
    }  // Both destroyed here
    
    cout << "\nNotifying (no observers left):" << endl;
    subject.notifyAll("Anyone there?");
}

// ============================================================
// Exercise 12: Aliasing Constructor ⭐⭐⭐
// ============================================================
/**
 * Use shared_ptr aliasing to point to a member.
 */
struct Container {
    int value;
    string name;
    
    Container(int v, const string& n) : value(v), name(n) {
        cout << "  Container created" << endl;
    }
    ~Container() {
        cout << "  Container destroyed" << endl;
    }
};

void exercise12() {
    shared_ptr<Container> container = make_shared<Container>(42, "MyContainer");
    
    // TODO: Create an aliasing shared_ptr that points to container->value
    // but shares ownership with container
    // Hint: shared_ptr<int> aliasPtr(container, &container->value);
    
    shared_ptr<int> valuePtr;  // Fix this
    
    if (valuePtr) {
        cout << "Value via alias: " << *valuePtr << endl;
        cout << "Container ref count: " << container.use_count() << " (expected: 2)" << endl;
    }
    
    cout << "Resetting container:" << endl;
    container.reset();
    
    if (valuePtr) {
        cout << "Value still accessible: " << *valuePtr << endl;
        cout << "valuePtr keeps Container alive" << endl;
    }
}

// ============================================================
// Exercise 13: enable_shared_from_this ⭐⭐⭐
// ============================================================
/**
 * Use enable_shared_from_this to get shared_ptr from this.
 */
class Gadget : public enable_shared_from_this<Gadget> {
    string name;
    
public:
    Gadget(const string& n) : name(n) {
        cout << "  Gadget '" << name << "' created" << endl;
    }
    
    ~Gadget() {
        cout << "  Gadget '" << name << "' destroyed" << endl;
    }
    
    // TODO: Implement this function to return shared_ptr to this
    shared_ptr<Gadget> getShared() {
        return nullptr;  // Fix: use shared_from_this()
    }
    
    void introduce() {
        cout << "  I am: " << name << endl;
    }
};

void exercise13() {
    // Must be created as shared_ptr!
    shared_ptr<Gadget> gadget = make_shared<Gadget>("SmartGadget");
    
    cout << "Original ref count: " << gadget.use_count() << endl;
    
    // TODO: Get another shared_ptr from the gadget itself
    shared_ptr<Gadget> another = gadget->getShared();
    
    if (another) {
        cout << "Got shared_from_this, ref count: " << gadget.use_count() << " (expected: 2)" << endl;
        another->introduce();
    }
}

// ============================================================
// Exercise 14: unique_ptr with Polymorphism ⭐⭐
// ============================================================
/**
 * Use unique_ptr with polymorphic types.
 */
class Animal {
public:
    virtual ~Animal() = default;
    virtual void speak() const = 0;
};

class Dog : public Animal {
public:
    Dog() { cout << "  Dog created" << endl; }
    ~Dog() { cout << "  Dog destroyed" << endl; }
    void speak() const override { cout << "  Woof!" << endl; }
};

class Cat : public Animal {
public:
    Cat() { cout << "  Cat created" << endl; }
    ~Cat() { cout << "  Cat destroyed" << endl; }
    void speak() const override { cout << "  Meow!" << endl; }
};

void exercise14() {
    vector<unique_ptr<Animal>> animals;
    
    // TODO: Add a Dog and Cat to the vector
    // Hint: animals.push_back(make_unique<Dog>());
    
    cout << "\nAll animals speak:" << endl;
    for (const auto& animal : animals) {
        animal->speak();
    }
    
    cout << "\nClearing vector:" << endl;
}

// ============================================================
// Exercise 15: Resource Manager Class ⭐⭐⭐
// ============================================================
/**
 * Create a class that manages multiple resources using smart pointers.
 */
class ResourceManager {
    vector<unique_ptr<Resource>> ownedResources;
    vector<weak_ptr<Resource>> observedResources;
    
public:
    // TODO: Create a new resource and store it
    Resource* createResource(const string& name) {
        // Create unique_ptr, store it, return raw pointer for use
        return nullptr;  // Fix this
    }
    
    // TODO: Observe an external shared resource
    void observeResource(shared_ptr<Resource> res) {
        // Store as weak_ptr
    }
    
    // TODO: Get count of valid observed resources
    int countValidObserved() {
        int count = 0;
        // Loop through observedResources, count non-expired
        return count;
    }
    
    int countOwned() { return ownedResources.size(); }
};

void exercise15() {
    ResourceManager manager;
    
    cout << "Creating owned resources:" << endl;
    Resource* r1 = manager.createResource("Owned1");
    Resource* r2 = manager.createResource("Owned2");
    
    if (r1 && r2) {
        r1->use();
        r2->use();
    }
    cout << "Owned count: " << manager.countOwned() << " (expected: 2)" << endl;
    
    cout << "\nObserving external resources:" << endl;
    {
        auto external1 = make_shared<Resource>("External1");
        auto external2 = make_shared<Resource>("External2");
        
        manager.observeResource(external1);
        manager.observeResource(external2);
        
        cout << "Valid observed: " << manager.countValidObserved() << " (expected: 2)" << endl;
    }  // External resources destroyed
    
    cout << "Valid observed after scope: " << manager.countValidObserved() << " (expected: 0)" << endl;
    
    cout << "\nManager going out of scope:" << endl;
}

// ============================================================
// TEST RUNNER
// ============================================================

int main() {
    cout << "╔════════════════════════════════════════════════════════════╗" << endl;
    cout << "║              SMART POINTERS - EXERCISES                    ║" << endl;
    cout << "╚════════════════════════════════════════════════════════════╝" << endl;
    
    cout << "\n=== Exercise 1: Create unique_ptr ===" << endl;
    exercise1();
    
    cout << "\n=== Exercise 2: Transfer Ownership ===" << endl;
    exercise2();
    
    cout << "\n=== Exercise 3: Factory Function ===" << endl;
    exercise3();
    
    cout << "\n=== Exercise 4: unique_ptr Array ===" << endl;
    exercise4();
    
    cout << "\n=== Exercise 5: shared_ptr Basics ===" << endl;
    exercise5();
    
    cout << "\n=== Exercise 6: weak_ptr Usage ===" << endl;
    exercise6();
    
    cout << "\n=== Exercise 7: Break Circular Reference ===" << endl;
    exercise7();
    
    cout << "\n=== Exercise 8: Pass to Function ===" << endl;
    exercise8();
    
    cout << "\n=== Exercise 9: Custom Deleter ===" << endl;
    exercise9();
    
    cout << "\n=== Exercise 10: Container of shared_ptr ===" << endl;
    exercise10();
    
    cout << "\n=== Exercise 11: Observer Pattern ===" << endl;
    exercise11();
    
    cout << "\n=== Exercise 12: Aliasing Constructor ===" << endl;
    exercise12();
    
    cout << "\n=== Exercise 13: enable_shared_from_this ===" << endl;
    exercise13();
    
    cout << "\n=== Exercise 14: Polymorphism ===" << endl;
    exercise14();
    
    cout << "\n=== Exercise 15: Resource Manager ===" << endl;
    exercise15();
    
    cout << "\n╔════════════════════════════════════════════════════════════╗" << endl;
    cout << "║         Complete the TODO sections and re-run!             ║" << endl;
    cout << "╚════════════════════════════════════════════════════════════╝" << endl;
    
    return 0;
}

// ============================================================
// ANSWER KEY
// ============================================================
/*
// Exercise 1
unique_ptr<Resource> res = make_unique<Resource>("Exercise1");
res->use();

// Exercise 2
unique_ptr<Resource> transferred = move(original);

// Exercise 3
unique_ptr<Resource> createResource(const string& name) {
    return make_unique<Resource>(name);
}

// Exercise 4
unique_ptr<int[]> arr = make_unique<int[]>(5);
for (int i = 0; i < 5; i++) arr[i] = (i + 1) * 10;
for (int i = 0; i < 5; i++) cout << arr[i] << " ";

// Exercise 5
shared_ptr<Resource> sp1 = make_shared<Resource>("Shared");
shared_ptr<Resource> sp2 = sp1;
shared_ptr<Resource> sp3 = sp1;
sp2.reset();

// Exercise 6
wp = sp;
if (auto locked = wp.lock()) { locked->use(); }
if (auto locked = wp.lock()) { locked->use(); } else { cout << "Expired" << endl; }

// Exercise 7
weak_ptr<Node> prev;  // Change shared_ptr to weak_ptr

// Exercise 8
useResource(*res);
takeResource(move(res));

// Exercise 9
shared_ptr<int> ptr(new int(42), [](int* p) {
    cout << "Custom deleter called!" << endl;
    delete p;
});

// Exercise 10
resources.push_back(make_shared<Resource>("Res1"));
resources.push_back(make_shared<Resource>("Res2"));
resources.push_back(make_shared<Resource>("Res3"));
shared_ptr<Resource> external = resources[0];
cout << resources[0].use_count();

// Exercise 11
void addObserver(shared_ptr<Observer> obs) {
    observers.push_back(obs);
}
void notifyAll(const string& message) {
    for (auto& wp : observers) {
        if (auto sp = wp.lock()) {
            sp->notify(message);
        }
    }
}

// Exercise 12
shared_ptr<int> valuePtr(container, &container->value);

// Exercise 13
shared_ptr<Gadget> getShared() {
    return shared_from_this();
}

// Exercise 14
animals.push_back(make_unique<Dog>());
animals.push_back(make_unique<Cat>());

// Exercise 15
Resource* createResource(const string& name) {
    auto res = make_unique<Resource>(name);
    Resource* ptr = res.get();
    ownedResources.push_back(move(res));
    return ptr;
}
void observeResource(shared_ptr<Resource> res) {
    observedResources.push_back(res);
}
int countValidObserved() {
    int count = 0;
    for (auto& wp : observedResources) {
        if (!wp.expired()) count++;
    }
    return count;
}
*/
Exercises - C++ Tutorial | DeepML