如何:创建和使用shared_ptr实例

shared_ptr 类型是智能指针在为方案设计多个所有者可能必须管理对象生存期内存中的 C++ 标准库中。在初始化可以将它复制的 shared_ptr 后,将它在函数参数的值,并将其分配给其他 shared_ptr 实例。所有实例指向同一对象,并且,对“的共享访问控制块”该引用计数的增量和减量,每当新 shared_ptr 添加,超出范围或重新设置。当引用计数达到零时,控制块删除内存资源和自身。

下图显示了指向个内存位置的几 shared_ptr 实例。

共享指针

示例

只要有可能,那么,当内存资源第一次时,创建请使用 make_shared (<memory>) 函数创建 shared_ptrmake_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的元素,然后将其复制到完全理解的其他容器基础内存有效,只要不需要且不再。下面的示例在矢量演示如何使用在 shared_ptr 实例的 replace_copy_if 算法。

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_each(v2.begin(), v2.end(), [] (shared_ptr<Song> s)
{
    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_each(photos.begin(), photos.end(), [] (shared_ptr<MediaAsset> p)
    {
        // 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,确定被调用方是否必须共享这种基础资源的所有权。“所有者”是可以使这种基础资源运行的对象或函数,只要它需要它。如果调用方必须确保则被调用方可以扩展指针的生存期在其 (函数) 的生存期以外的,请使用第一个选项。如果您不关心被调用方是否扩展生存期,则可以通过引用并允许被调用方复制它。

  • 如果必须允许帮助器到基础指针的函数访问,因此,您了解 helper 函数将使用指针和返回,在被调用函数返回以前,则该函数不必共享基础指针的所有权。它必须对调用方的 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; 

请参见

概念

智能指针(现代C++)