Algorithms, Blockchain and Cloud

Tutorial on C++ Smart Pointers


Tutorial on Smart Pointers in C++

Smart pointers in C++ provide automatic and safe memory management. They help avoid memory leaks and dangling pointers by ensuring proper object destruction through RAII (Resource Acquisition Is Initialization).

This tutorial covers the three primary smart pointers in C++:

  • std::unique_ptr
  • std::shared_ptr
  • std::weak_ptr

1. std::unique_ptr

unique_ptr has exclusive ownership. Only one unique pointer can own a resource at a time.

Example: Owning a Simple Object

1
2
3
4
5
6
7
8
9
10
11
12
#include <iostream>
#include <memory>
 
int main() {
    std::unique_ptr<int> p = std::make_unique<int>(42);
    std::cout << "Value: " << *p << "\n";
 
    // Transfer ownership
    std::unique_ptr<int> q = std::move(p);
    if (!p) std::cout << "p is now null\n";
    std::cout << "q points to: " << *q << "\n";
}
#include <iostream>
#include <memory>

int main() {
    std::unique_ptr<int> p = std::make_unique<int>(42);
    std::cout << "Value: " << *p << "\n";

    // Transfer ownership
    std::unique_ptr<int> q = std::move(p);
    if (!p) std::cout << "p is now null\n";
    std::cout << "q points to: " << *q << "\n";
}

Example: Building a Linked List

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
struct Node {
    int val;
    std::unique_ptr<Node> next;
    Node(int v) : val(v), next(nullptr) {}
};
 
void printList(const std::unique_ptr<Node>& head) {
    const Node* curr = head.get();
    while (curr) {
        std::cout << curr->val << " ";
        curr = curr->next.get();
    }
    std::cout << "\n";
}
 
int main() {
    auto head = std::make_unique<Node>(1);
    head->next = std::make_unique<Node>(2);
    head->next->next = std::make_unique<Node>(3);
 
    printList(head);
}
struct Node {
    int val;
    std::unique_ptr<Node> next;
    Node(int v) : val(v), next(nullptr) {}
};

void printList(const std::unique_ptr<Node>& head) {
    const Node* curr = head.get();
    while (curr) {
        std::cout << curr->val << " ";
        curr = curr->next.get();
    }
    std::cout << "\n";
}

int main() {
    auto head = std::make_unique<Node>(1);
    head->next = std::make_unique<Node>(2);
    head->next->next = std::make_unique<Node>(3);

    printList(head);
}

2. std::shared_ptr

shared_ptr allows multiple owners of a resource. It maintains a reference count and deletes the object when the last reference is gone.

Example: Shared Ownership

1
2
3
4
5
6
7
8
9
10
11
#include <iostream>
#include <memory>
 
int main() {
    std::shared_ptr<int> a = std::make_shared<int>(100);
    std::shared_ptr<int> b = a;
 
    std::cout << "a use count: " << a.use_count() << "\n";
    std::cout << "b use count: " << b.use_count() << "\n";
    std::cout << "*b = " << *b << "\n";
}
#include <iostream>
#include <memory>

int main() {
    std::shared_ptr<int> a = std::make_shared<int>(100);
    std::shared_ptr<int> b = a;

    std::cout << "a use count: " << a.use_count() << "\n";
    std::cout << "b use count: " << b.use_count() << "\n";
    std::cout << "*b = " << *b << "\n";
}

Example: Shared Linked List

1
2
3
4
5
struct Node {
    int val;
    std::shared_ptr<Node> next;
    Node(int v) : val(v), next(nullptr) {}
};
struct Node {
    int val;
    std::shared_ptr<Node> next;
    Node(int v) : val(v), next(nullptr) {}
};

3. std::weak_ptr

weak_ptr is a non-owning reference to an object managed by shared_ptr. It is used to prevent circular references that lead to memory leaks.

Example: Breaking a Circular Reference

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#include <iostream>
#include <memory>
 
struct B;
 
struct A {
    std::shared_ptr<B> b_ptr;
    ~A() { std::cout << "A destroyed\n"; }
};
 
struct B {
    std::weak_ptr<A> a_ptr;
    ~B() { std::cout << "B destroyed\n"; }
};
 
int main() {
    auto a = std::make_shared<A>();
    auto b = std::make_shared<B>();
    a->b_ptr = b;
    b->a_ptr = a;
}
#include <iostream>
#include <memory>

struct B;

struct A {
    std::shared_ptr<B> b_ptr;
    ~A() { std::cout << "A destroyed\n"; }
};

struct B {
    std::weak_ptr<A> a_ptr;
    ~B() { std::cout << "B destroyed\n"; }
};

int main() {
    auto a = std::make_shared<A>();
    auto b = std::make_shared<B>();
    a->b_ptr = b;
    b->a_ptr = a;
}

Is There a make_weak?

No, there is no std::make_weak. This is because weak_ptr does not own memory and must point to an existing shared_ptr.

How to Create a weak_ptr

1
2
3
4
5
6
7
8
9
10
11
12
13
#include <iostream>
#include <memory>
 
int main() {
    std::shared_ptr<int> sp = std::make_shared<int>(42);
    std::weak_ptr<int> wp = sp;
 
    if (auto locked = wp.lock()) {
        std::cout << "Value: " << *locked << "\n";
    } else {
        std::cout << "Object no longer exists\n";
    }
}
#include <iostream>
#include <memory>

int main() {
    std::shared_ptr<int> sp = std::make_shared<int>(42);
    std::weak_ptr<int> wp = sp;

    if (auto locked = wp.lock()) {
        std::cout << "Value: " << *locked << "\n";
    } else {
        std::cout << "Object no longer exists\n";
    }
}

Summary

Smart Pointer Ownership Thread Safe Ref Counting Use Case
unique_ptr Exclusive N/A Fast, safe, sole ownership
shared_ptr Shared Yes Shared ownership
weak_ptr None N/A Break circular refs in shared_ptr

C/C++ Programming

–EOF (The Ultimate Computing & Technology Blog) —

831 words
Last Post: From Idea to GitHub Pages: Building Tools with AI and Vibe Coding
Next Post: Tutorial on C++ Ranges

The Permanent URL is: Tutorial on C++ Smart Pointers (AMP Version)

Exit mobile version