[C++] How to Use std::shared_ptr | Smart Pointers for Shared Ownership

目次

Introduction

std::unique_ptr in C++ is a smart pointer that guarantees a resource has only one owner. However, there are times when you want to share and use the same object from multiple locations.

std::shared_ptr realizes this “shared ownership.” std::shared_ptr maintains a mechanism internally called a “reference counter,” which counts how many shared_ptr instances are pointing to the same object. The moment the last shared_ptr is destroyed and the reference counter reaches zero, the memory of the object is automatically released.

In this article, I will explain the main usages of shared_ptr:

  • Basic Usage (Reference Counting)
  • Recommended Creation Method (std::make_shared)
  • Resetting Resources (.reset)
  • Specifying Custom Deleters

Prerequisite: What is C++11?

C++11 is a major update to the C++ language formalized in 2011. Since shared_ptr was introduced in C++11, you need a compiler that supports C++11 or later to use it.

1. Basic Usage (Reference Counting)

shared_ptr can be copied, and the reference counter increases each time it is copied.

Sample Code

#include <iostream>
#include <memory> // shared_ptr
#include <string>

using namespace std;

struct Resource {
    string name;
    Resource(string n) : name(n) { cout << "Resource acquired" << endl; }
    ~Resource() { cout << "Resource released" << endl; }
};

int main() {
    shared_ptr<Resource> p1; // Empty shared_ptr
    
    cout << "--- Entering block ---" << endl;
    {
        shared_ptr<Resource> p2 = make_shared<Resource>("Data A");
        cout << "Reference count: " << p2.use_count() << endl; // -> 1
        
        p1 = p2; // Copy. p1 and p2 point to the same object.
        cout << "Reference count: " << p2.use_count() << endl; // -> 2
    } // p2 leaves scope, but p1 still owns it, so the resource is NOT released.
    cout << "--- Exited block ---" << endl;
    
    cout << "Reference count: " << p1.use_count() << endl; // -> 1
    
    return 0;
    // When exiting the main function, p1 is destroyed, the reference count becomes 0,
    // and the resource is released for the first time here.
}

2. Creation via std::make_shared

When creating a shared_ptr, it is strongly recommended to use the std::make_shared helper function rather than using new directly. make_shared allocates memory for both the object body and the management block (reference counter, etc.) at once, making it more efficient and increasing exception safety.

Sample Code

// Deprecated way
shared_ptr<Resource> p1(new Resource("Data B"));

// Recommended way (C++14 and later)
auto p2 = make_shared<Resource>("Data C");

Explanation: You pass the arguments for the constructor of the class you want to create directly to make_shared.

3. Resetting Resources (.reset)

Using .reset(), you can release the object currently pointed to by the shared_ptr and change it to point to a new object. Calling it without arguments makes the pointer empty.

auto p = make_shared<Resource>("Data D");
cout << "Reference count: " << p.use_count() << endl; // -> 1

p.reset(new Resource("Data E")); // "Data D" is released here
cout << "After reset, p points to Data E." << endl;

p.reset(); // "Data E" is released here, and p becomes empty
cout << "After reset with no args, is p empty? " << (p ? "No" : "Yes") << endl;

4. Specifying Custom Deleters

Just like unique_ptr, you can specify a custom deleter to release resources using methods other than delete.

#include <cstdio>

// Custom deleter function to fclose FILE*
void file_closer(FILE* fp) {
    if (fp) {
        fclose(fp);
        cout << "Custom Deleter: File closed." << endl;
    }
}

int main() {
    // Pass the deleter as the second argument to the shared_ptr constructor
    shared_ptr<FILE> file_ptr(fopen("test.txt", "w"), file_closer);

    if (file_ptr) {
        fwrite("hello", 1, 5, file_ptr.get());
    }
    // When file_ptr is destroyed, file_closer(fp) is automatically called
    return 0;
}

Summary

In this article, I explained the basics of memory management with shared ownership using C++11 std::shared_ptr.

  • shared_ptr safely shares a single object among multiple pointers via reference counting.
  • The resource is automatically released when the last owner is gone.
  • Basically, use make_shared for safe and efficient creation.
  • Since it has slightly more overhead than unique_ptr, it is important to consider “is shared ownership really necessary?” before using it.

shared_ptr is an essential tool in modern C++ for expressing complex object ownership relationships simply and safely.

よかったらシェアしてね!
  • URLをコピーしました!
  • URLをコピーしました!

この記事を書いた人

私が勉強したこと、実践したこと、してることを書いているブログです。
主に資産運用について書いていたのですが、
最近はプログラミングに興味があるので、今はそればっかりです。

目次