cpp
examples
examples.cpp⚙️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;
}