Search Knowledge

© 2026 LIBREUNI PROJECT

Modern C++ Programming / Modern C++ Features

Smart Pointers and Memory Management

Smart Pointers: Automated Memory Management

Raw pointers require manual memory management with new/delete, leading to leaks, double-deletes, and dangling pointers. Smart pointers from <memory> automate lifetime management through RAII.

unique_ptr: Exclusive Ownership

unique_ptr<T> represents exclusive ownership: exactly one owner, non-copyable but movable. The pointed-to object is destroyed when the unique_ptr is destroyed.

#include <memory>

std::unique_ptr<int> p1(new int(42));
// std::unique_ptr<int> p2 = p1;  // Error: cannot copy

std::unique_ptr<int> p2 = std::move(p1);  // OK: transfer ownership
// p1 is now nullptr

auto p3 = std::make_unique<int>(100);  // Preferred (C++14)

Why make_unique?

// Problematic:
process(std::unique_ptr<T>(new T), might_throw());
// If might_throw() throws after new but before unique_ptr construction, leak!

// Safe:
process(std::make_unique<T>(), might_throw());
// make_unique is atomic: allocation and unique_ptr construction happen together

unique_ptr for Arrays

std::unique_ptr<int[]> arr(new int[10]);
arr[0] = 5;  // Operator[] available for array version

// Prefer std::vector or std::array
std::vector<int> vec(10);  // Better

Custom Deleters

struct FileCloser {
    void operator()(FILE* fp) const {
        if (fp) fclose(fp);
    }
};

std::unique_ptr<FILE, FileCloser> file(fopen("data.txt", "r"));
// File automatically closed when unique_ptr destroyed

shared_ptr: Shared Ownership

shared_ptr<T> uses reference counting: the object is destroyed when the last shared_ptr owning it is destroyed. Copyable and movable.

std::shared_ptr<int> p1 = std::make_shared<int>(42);
std::shared_ptr<int> p2 = p1;  // Both own the object
// Reference count: 2

p1.reset();  // p1 releases ownership, count = 1
// p2 still valid, object alive

Control Block Architecture

Each shared_ptr has two pointers:

  1. Pointer to the managed object
  2. Pointer to the control block (reference counts, deleter)
Interactive Lab

Creating shared_ptr

// Preferred: single allocation for object + control block
auto p1 = <int>(42);

// Avoid: two allocations
std::shared_ptr<int> p2(new int(42));

Cyclic References Problem

struct Node {
    std::shared_ptr<Node> next;
};

std::shared_ptr<Node> a = std::make_shared<Node>();
std::shared_ptr<Node> b = std::make_shared<Node>();

a->next = b;  // a owns b, count = 2
b->next = a;  // b owns a, count = 2
// Neither can be destroyed: memory leak!

weak_ptr: Breaking Cycles

weak_ptr<T> observes a shared_ptr without affecting reference count. Used to break cycles and check if object still exists.

struct Node {
    std::shared_ptr<Node> next;
    std::weak_ptr<Node> prev;  // Doesn't own
};

std::shared_ptr<Node> a = std::make_shared<Node>();
std::shared_ptr<Node> b = std::make_shared<Node>();

a->next = b;
b->prev = a;  // Weak reference: no cycle

Using weak_ptr

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

if (auto locked = wp.lock()) {  // Attempt to get shared_ptr
    std::cout << *locked << '\\n';  // Object still exists
} else {
    std::cout << "Object destroyed\\n";
}

Comparison: unique_ptr vs shared_ptr

Aspectunique_ptrshared_ptr
OwnershipExclusiveShared
CopyableNoYes
SizeSize of pointer2 pointers
OverheadZeroReference counting (atomic for thread safety)
Use WhenClear single ownerUnclear ownership, need sharing

Rule of Thumb: Default to unique_ptr. Use shared_ptr only when ownership truly must be shared.

Returning Smart Pointers from Functions

// Return unique_ptr when transferring ownership
std::unique_ptr<Widget> createWidget() {
    return std::make_unique<Widget>();
}

auto w = createWidget();  // Ownership transferred via move

// Prefer returning by value for copyable types
Widget createWidget2() {
    return Widget{};  // Copy elision / move
}

Smart Pointers and Polymorphism

class Base { 
public:
    virtual ~Base() = default; 
};
class Derived : public Base {};

std::unique_ptr<Base> p = std::make_unique<Derived>();
// Polymorphism works: Derived destructor called when p destroyed

Common Pitfalls

Constructing shared_ptr from Same Raw Pointer Twice

int* raw = new int(42);
std::shared_ptr<int> p1(raw);
std::shared_ptr<int> p2(raw);  // DISASTER: double-delete!

// Each shared_ptr has independent control block

Never create multiple shared_ptr from the same raw pointer. Use make_shared or copy existing shared_ptr.

Conceptual Check

What happens when you copy a unique_ptr?

Runtime Environment

Interactive Lab

1#include <iostream>
2#include <memory>
3 
4class Resource {
5 int id_;
6public:
7 Resource(int id) : id_(id) { std::cout << "Resource " << id_ << " created\n"; }
8 ~Resource() { std::cout << "Resource " << id_ << " destroyed\n"; }
9 int id() const { return id_; }
10};
11 
12int main() {
13 {
14 auto p1 = std::make_unique<Resource>(1);
15 auto p2 = std::make_shared<Resource>(2);
16 auto p3 = p2; // Shared ownership
17 std::cout << "Ref count: " << p2.use_count() << '\n';
18 } // All destroyed here
19 std::cout << "Scope exited\n";
20 return 0;
21}
System Console

Waiting for signal...