如何:创建和使用 shared_ptr 实例
shared_ptr 的类型是C + +标准库中一个聪明的指针,是为多个拥有者管理内存中对象的生命周期而设计的。 在你初始化一个 shared_ptr 后,你可以复制它,把函数参数的值递给它,并把它分配给其它 shared_ptr 实例。 所有实例指向同一个对象,并共享访问一个“控制块”,即每当一个新的 shared_ptr 被添加时,递增和递减引用计数,超出范围,则复位。 当引用计数到达零时,控制块删除内存资源和自身。
下图显示了指向一个内存位置的几个 shared_ptr 实例。
示例
无论什么时候,当内存资源被第一次被创建时,就使用函数 make_shared (<memory>) 创建一个新的 shared_ptr。 make_shared异常安全。 它使用同一调用分配的内存控制块和资源从而减少构造开销。 如果你不使用 make_shared,那么在把它传递给 shared_ptr 的构造函数之前,你必须使用一个明确的新表达式创建的对象。 下面的例子显示了在新对象中声明和初始化一个 shared_ptr 的各种方式。
// 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");
下面的示例演示如何声明和初始化一个已经被分配了另一个 shared_ptr 的对象共享所有权的 shared_ptr 的实例。 假设 sp2 是一个初始化的 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);
当您使用算法复制元素时,shared_ptr 的也是很有用的标准模板库(STL)。 你可以把元素包装在 shared_ptr 里,然后将其复制到其他容器,只要你需要它,底层的内存始终是有效的。 以下示例演示如何使用 replace_copy_if 算法来创建一个 shared_ptr 的实例以及如何在一个向量上进行使用。
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;
}
你可以用 dynamic_pointer_cast, static_pointer_cast 和 const_pointer_cast 来转换shared_ptr。 这些函数的操作类似 dynamic_cast, static_cast 和 const_cast。 下面的示例演示如何测试在基类的 shared_ptr 向量中的每个元素的派生类,,然后复制元素,并显示它们的信息。
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;
}
你可以用下列方法把 shared_ptr 传递给另一个函数:
向 shared_ptr 传递值。 调用复制构造函数,递增引用计数,并把被调用方当做所有者。 还有就是在这次操作中有少量的开销,这很大程度上取决于你传递了多少 shared_ptr 对象。 当调用方和被调用方之间的代码协定 (隐式或显式) 要求被调用方是所有者,使用此选项。
通过引用或常量引用来传递 shared_ptr。 在这种情况下,引用计数不增加,并且只要调用方不超出范围,被调用方就可以访问指针。 或者,被调用方可以决定创建一个基于引用的 shared_ptr,从而成为一个共享所有者。 当调用者并不知道被被调用方,或当您必须传递一个 shared_ptr,并希望避免由于性能原因的复制操作,请使用此选项。
通过底层的指针或引用底层的对象。 这使得被调用方使用对象,但不使共享所有权或扩展生存期。 如果被调用方从原始指针创建一个 shared_ptr,则新的 shared_ptr 是独立于原来的,且没有控制底层的资源。 当调用方和被调用方之间的协定中明确规定调用者保留 shared_ptr 生存期的所有权,则使用此选项。
当您决定如何传递一个 shared_ptr时,确定被调用方是否有共享基础资源的所有权。 一个“所有者”就是只要它需要就可以使用底层资源的对象或函数。 如果调用方必须保证被调用方可以在其(函数)生存期以外扩展指针的生存期,请使用第一个选项。 如果您不关心被调用方是否扩展生存期,则通过引用传递并让被调用方复制它。
如果不得不允许帮助程序函数访问底层指针,并且您知道帮助程序函数将使用指针且在调用函数返回前先返回,则该函数不必共享底层指针的所有权。 仅仅是在调用方的 shared_ptr 的生存期内允许访问指针。 在这种情况下,通过引用来传递 shared_ptr,通过原始指针或引用的基本对象都是安全的。 通过此方式提供一个小的性能改进,并且还有助于表示程序的意图。
有时,例如在一个 **std:vector<shared_ptr<T>>**中,您可能必须对传递每个 shared_ptr 给lambda表达式体或命名函数对象。 如果lambda或函数没有存储指针,则通过引用传递 shared_ptr,以避免调用拷贝构造函数的每个元素。
下面的示例显示 shared_ptr 如何重载多种比较操作符,以使由 shared_ptr 实例所拥有的内存指针的比较。
// 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;