C++ 共享所有权(std :: shared_ptr)

示例

类模板std::shared_ptr定义了一个共享指针,该共享指针能够与其他共享指针共享对象的所有权。这与std::unique_ptr代表专有所有权相反。

共享行为是通过一种称为引用计数的技术来实现的,其中将指向对象的共享指针的数量存储在对象旁边。当此计数达到零时(通过销毁或重新分配最后一个std::shared_ptr实例),该对象将自动销毁。


// 创建:“ firstShared”是“ Foo”新实例的共享指针
std::shared_ptr<Foo> firstShared = std::make_shared<Foo>(/*args*/);

要创建共享同一对象的多个智能指针,我们需要创建另一个shared_ptr别名作为第一个共享指针的别名。这有两种方法:

std::shared_ptr<Foo> secondShared(firstShared);  // 第一种方式:复制构造
std::shared_ptr<Foo> secondShared;
secondShared = firstShared;                      // 第二种方式:分配

上述任何一种方法,使secondShared一个共享指针,我们的情况下持有股票的Foo有firstShared。

智能指针就像原始指针一样工作。这意味着,您可以*用来取消引用它们。常规->运算符也可以正常工作:

secondShared->test(); // 调用Foo :: test()

最后,当最后一个别名shared_ptr超出范围时,将Foo调用我们实例的析构函数。

警告:当需要分配用于共享所有权语义的额外数据时,构造ashared_ptr可能会引发bad_alloc异常。如果向构造函数传递了常规指针,则它假定拥有所指向的对象,并在引发异常时调用删除程序。这意味着如果分配失败,将不会泄漏对象。但是,建议使用或,它们使实现能够优化内存分配。shared_ptr<T>(new T(args))Tshared_ptr<T>make_shared<T>(args)allocate_shared<T>(alloc, args)


使用shared_ptr分配数组([])

C ++ 11 C ++ 17

不幸的是,没有直接的方法使用分配数组make_shared<>。

可以shared_ptr<>使用new和创建数组std::default_delete。

例如,要分配10个整数的数组,我们可以将代码编写为

shared_ptr<int> sh(new int[10], std::default_delete<int[]>());

std::default_delete在此处必须进行指定,以确保使用正确清除了分配的内存delete[]。

如果我们在编译时知道大小,则可以这样进行:

template<class Arr>
struct shared_array_maker {};
template<class T, std::size_t N>
struct shared_array_maker<T[N]> {
  std::shared_ptr<T> operator()const{
    auto r = std::make_shared<std::array<T,N>>();
    if (!r) return {};
    return {r.data(), r};
  }
};
template<class Arr>
auto make_shared_array()
-> decltype( shared_array_maker<Arr>{}() )
{ return shared_array_maker<Arr>{}(); }

然后make_shared_array<int[10]>返回一个shared_ptr<int>指向所有默认构造的10个整数的指针。

C ++ 17

使用C ++ 17,shared_ptr获得了对数组类型的特殊支持。不再需要显式指定数组派发器,并且可以使用[]数组索引运算符取消引用共享指针:

std::shared_ptr<int[]> sh(new int[10]);
sh[0] = 42;

共享的指针可以指向它拥有的对象的子对象:

struct Foo { int x; };
std::shared_ptr<Foo> p1 = std::make_shared<Foo>();
std::shared_ptr<int> p2(p1, &p1->x);

这两个p2和p1自己的类型的对象Foo,但p2指向它的int成员x。这意味着,如果p1超出范围或被重新分配,则基础Foo对象仍将保持活动状态,确保p2不会悬空。


重要提示: Ashared_ptr仅了解自身以及shared_ptr使用别名构造函数创建的所有其他信息。它不知道任何其他指针,包括shared_ptr通过引用同一Foo实例创建的所有其他指针:

Foo *foo = new Foo;
std::shared_ptr<Foo> shared1(foo);
std::shared_ptr<Foo> shared2(foo); // 不要这样做

shared1.reset(); // 这将删除foo,因为shared1
                 // 是拥有它的唯一shared_ptr

shared2->test(); // 不确定的行为:shared2的foo被
                 // 已删除!

shared_ptr的所有权转移

默认情况下,shared_ptr增加引用计数并且不转移所有权。但是,可以使用std::move以下方式转移所有权:

shared_ptr<int> up = make_shared<int>();
// 转让所有权
shared_ptr<int> up2 = move(up);
// 此时,参考计数up = 0,并且
// 指针的所有权仅与引用计数= 1的up2一起