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_ptrsafely shares a single object among multiple pointers via reference counting.- The resource is automatically released when the last owner is gone.
- Basically, use
make_sharedfor 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.
