cpp
exercises
exercises.cpp⚙️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;
}
*/