How to: Create and Use shared_ptr Instances
The new home for Visual Studio documentation is Visual Studio 2017 Documentation on docs.microsoft.com.
The latest version of this topic can be found at How to: Create and Use shared_ptr Instances. The shared_ptr
type is a smart pointer in the C++ standard library that is designed for scenarios in which more than one owner might have to manage the lifetime of the object in memory. After you initialize a shared_ptr
you can copy it, pass it by value in function arguments, and assign it to other shared_ptr
instances. All the instances point to the same object, and share access to one "control block" that increments and decrements the reference count whenever a new shared_ptr
is added, goes out of scope, or is reset. When the reference count reaches zero, the control block deletes the memory resource and itself.
The following illustration shows several shared_ptr
instances that point to one memory location.
Example
Whenever possible, use the make_shared function to create a shared_ptr
when the memory resource is created for the first time. make_shared
is exception-safe. It uses the same call to allocate the memory for the control block and the resource, and thereby reduces the construction overhead. If you do not use make_shared
, then you have to use an explicit new expression to create the object before you pass it to the shared_ptr
constructor. The following example shows various ways to declare and initialize a shared_ptr
together with a new object.
// Use make_shared function when possible.
auto sp1 = make_shared<Song>(L"The Beatles", L"Im Happy Just to Dance With You");
// Ok, but slightly less efficient.
// Note: Using new expression as constructor argument
// creates no named variable for other code to access.
shared_ptr<Song> sp2(new Song(L"Lady Gaga", L"Just Dance"));
// When initialization must be separate from declaration, e.g. class members,
// initialize with nullptr to make your programming intent explicit.
shared_ptr<Song> sp5(nullptr);
//Equivalent to: shared_ptr<Song> sp5;
//...
sp5 = make_shared<Song>(L"Elton John", L"I'm Still Standing");
Example
The following example shows how to declare and initialize shared_ptr
instances that take on shared ownership of an object that has already been allocated by another shared_ptr
. Assume that sp2
is an initialized shared_ptr
.
//Initialize with copy constructor. Increments ref count.
auto sp3(sp2);
//Initialize via assignment. Increments ref count.
auto sp4 = sp2;
//Initialize with nullptr. sp7 is empty.
shared_ptr<Song> sp7(nullptr);
// Initialize with another shared_ptr. sp1 and sp2
// swap pointers as well as ref counts.
sp1.swap(sp2);
Example
shared_ptr
is also helpful in Standard Template Library (STL) containers when you are using algorithms that copy elements. You can wrap elements in a shared_ptr
, and then copy it into other containers with the understanding that the underlying memory is valid as long as you need it, and no longer. The following example shows how to use the replace_copy_if
algorithm on shared_ptr
instances in a vector.
vector<shared_ptr<Song>> v;
v.push_back(make_shared<Song>(L"Bob Dylan", L"The Times They Are A Changing"));
v.push_back(make_shared<Song>(L"Aretha Franklin", L"Bridge Over Troubled Water"));
v.push_back(make_shared<Song>(L"Thal�a", L"Entre El Mar y Una Estrella"));
vector<shared_ptr<Song>> v2;
remove_copy_if(v.begin(), v.end(), back_inserter(v2), [] (shared_ptr<Song> s)
{
return s->artist.compare(L"Bob Dylan") == 0;
});
for (const auto& s : v2)
{
wcout << s->artist << L":" << s->title << endl;
}
Example
You can use dynamic_pointer_cast
, static_pointer_cast
, and const_pointer_cast
to cast a shared_ptr
. These functions resemble the dynamic_cast
, static_cast
, and const_cast
operators. The following example shows how to test the derived type of each element in a vector of shared_ptr
of base classes, and then copy the elements and display information about them.
vector<shared_ptr<MediaAsset>> assets;
assets.push_back(shared_ptr<Song>(new Song(L"Himesh Reshammiya", L"Tera Surroor")));
assets.push_back(shared_ptr<Song>(new Song(L"Penaz Masani", L"Tu Dil De De")));
assets.push_back(shared_ptr<Photo>(new Photo(L"2011-04-06", L"Redmond, WA", L"Soccer field at Microsoft.")));
vector<shared_ptr<MediaAsset>> photos;
copy_if(assets.begin(), assets.end(), back_inserter(photos), [] (shared_ptr<MediaAsset> p) -> bool
{
// Use dynamic_pointer_cast to test whether
// element is a shared_ptr<Photo>.
shared_ptr<Photo> temp = dynamic_pointer_cast<Photo>(p);
return temp.get() != nullptr;
});
for (const auto& p : photos)
{
// We know that the photos vector contains only
// shared_ptr<Photo> objects, so use static_cast.
wcout << "Photo location: " << (static_pointer_cast<Photo>(p))->location_ << endl;
}
Example
You can pass a shared_ptr
to another function in the following ways:
Pass the
shared_ptr
by value. This invokes the copy constructor, increments the reference count, and makes the callee an owner. There is a small amount of overhead in this operation, which may be significant depending on how manyshared_ptr
objects you are passing. Use this option when the code contract (implied or explicit) between the caller and callee requires that the callee be an owner.Pass the
shared_ptr
by reference or const reference. In this case, the reference count is not incremented, and the callee can access the pointer as long as the caller does not go out of scope. Or, the callee can decide to create ashared_ptr
based on the reference, and thereby become a shared owner. Use this option when the caller has no knowledge of the callee, or when you must pass ashared_ptr
and want to avoid the copy operation for performance reasons.Pass the underlying pointer or a reference to the underlying object. This enables the callee to use the object, but does not enable it to share ownership or extend the lifetime. If the callee creates a
shared_ptr
from the raw pointer, the newshared_ptr
is independent from the original, and does not control the underlying resource. Use this option when the contract between the caller and callee clearly specifies that the caller retains ownership of theshared_ptr
lifetime.When you are deciding how to pass a
shared_ptr
, determine whether the callee has to share ownership of the underlying resource. An "owner" is an object or function that can keep the underlying resource alive for as long as it needs it. If the caller has to guarantee that the callee can extend the life of the pointer beyond its (the function's) lifetime, use the first option. If you don't care whether the callee extends the lifetime, then pass by reference and let the callee copy it or not.If you have to give a helper function access to the underlying pointer, and you know that the helper function will just use the pointer and return before the calling function returns, then that function does not have to share ownership of the underlying pointer. It just has to access the pointer within the lifetime of the caller's
shared_ptr
. In this case, it is safe to pass theshared_ptr
by reference, or pass the raw pointer or a reference to the underlying object. Passing this way provides a small performance benefit, and may also help you express your programming intent.Sometimes, for example in a
std:vector<shared_ptr<T>>
, you may have to pass eachshared_ptr
to a lambda expression body or named function object. If the lambda or function is not storing the pointer, then pass theshared_ptr
by reference to avoid invoking the copy constructor for each element.
Example
The following example shows how shared_ptr
overloads various comparison operators to enable pointer comparisons on the memory that is owned by the shared_ptr
instances.
// Initialize two separate raw pointers.
// Note that they contain the same values.
auto song1 = new Song(L"Village People", L"YMCA");
auto song2 = new Song(L"Village People", L"YMCA");
// Create two unrelated shared_ptrs.
shared_ptr<Song> p1(song1);
shared_ptr<Song> p2(song2);
// Unrelated shared_ptrs are never equal.
wcout << "p1 < p2 = " << std::boolalpha << (p1 < p2) << endl;
wcout << "p1 == p2 = " << std::boolalpha <<(p1 == p2) << endl;
// Related shared_ptr instances are always equal.
shared_ptr<Song> p3(p2);
wcout << "p3 == p2 = " << std::boolalpha << (p3 == p2) << endl;