SyntaxStudy
Sign Up
C++ shared_ptr and Reference Counting
C++ Beginner 1 min read

shared_ptr and Reference Counting

'std::shared_ptr' implements shared ownership through reference counting. Each 'shared_ptr' that points to an object increments an atomic reference count on construction and decrements it on destruction. When the count reaches zero — meaning no more 'shared_ptr' objects hold a reference — the managed object is deleted. This enables objects with multiple owners without manual lifetime management. 'std::make_shared' is the preferred factory: it allocates the managed object and the control block (which stores the reference count) in a single allocation, improving performance and cache locality compared to constructing the 'shared_ptr' from a raw 'new' expression. Always prefer 'make_shared' unless a custom deleter or a separate control block is required. Shared ownership has costs: the atomic reference count operations add overhead in multithreaded code, and cycles between 'shared_ptr' instances cause memory leaks because the reference counts never reach zero. Cycles are broken by replacing one of the 'shared_ptr' links with a 'std::weak_ptr'. A 'weak_ptr' observes an object without owning it, and must be converted to a 'shared_ptr' with 'lock()' before use.
Example
#include <iostream>
#include <memory>
#include <string>
#include <vector>

struct Resource {
    std::string name;
    explicit Resource(std::string n) : name(std::move(n)) {
        std::cout << "Resource '" << name << "' acquired\n";
    }
    ~Resource() {
        std::cout << "Resource '" << name << "' released\n";
    }
};

void useResource(std::shared_ptr<Resource> r) {
    std::cout << "Using: " << r->name
              << " (use_count=" << r.use_count() << ")\n";
}

int main() {
    // make_shared: single allocation for object + control block
    auto r1 = std::make_shared<Resource>("Database");
    std::cout << "r1 use_count = " << r1.use_count() << "\n";  // 1

    {
        auto r2 = r1;   // shared ownership — count becomes 2
        std::cout << "r1 use_count = " << r1.use_count() << "\n";  // 2
        useResource(r2);   // count temporarily 3 inside function
        std::cout << "r1 use_count = " << r1.use_count() << "\n";  // 2
    }
    // r2 out of scope — count back to 1
    std::cout << "r1 use_count = " << r1.use_count() << "\n";  // 1

    // Vector of shared_ptrs — all share the same Resource
    std::vector<std::shared_ptr<Resource>> holders;
    holders.push_back(r1);
    holders.push_back(r1);
    std::cout << "use_count with vector = " << r1.use_count() << "\n";  // 3

    holders.clear();   // removes two shares
    std::cout << "use_count after clear = " << r1.use_count() << "\n"; // 1

    return 0;   // Resource released here
}