c++ - 为什么在 std::shared_ptr 实现中需要两个指向托管对象的原始指针?

标签 c++ pointers c++11 shared-ptr

这是来自 std::shared_ptr 的 cppreference 实现说明部分的引用,其中提到有两个不同的指针(如粗体所示):一个可以由 get 返回(),以及在控制 block 中保存实际数据的那个。

In a typical implementation, std::shared_ptr holds only two pointers:

  1. the stored pointer (one returned by get())
  2. a pointer to control block

The control block is a dynamically-allocated object that holds:

  1. either a pointer to the managed object or the managed object itself
  2. the deleter (type-erased)
  3. the allocator (type-erased)
  4. the number of shared_ptrs that own the managed object
  5. the number of weak_ptrs that refer to the managed object

The pointer held by the shared_ptr directly is the one returned by get(), while the pointer or object held by the control block is the one that will be deleted when the number of shared owners reaches zero. These pointers are not necessarily equal.

我的问题是,为什么托管对象需要两个不同的指针(粗体中的两个)(除了指向控制 block 的指针)? get() 返回的还不够吗?为什么这些指针不一定相等?

最佳答案

这样做的原因是你可以有一个 shared_ptr 指向它所拥有的东西之外的东西,这是设计使然。这是使用列为 nr 的构造函数实现的。 8 上 cppreference :

template< class Y >
shared_ptr( const shared_ptr<Y>& r, T *ptr );

使用此构造函数创建的 shared_ptr r 共享所有权,但指向 ptr。考虑这个(人为的,但说明性的)代码:

std::shared_ptr<int> creator()
{
  using Pair = std::pair<int, double>;

  std::shared_ptr<Pair> p(new Pair(42, 3.14));
  std::shared_ptr<int> q(p, &(p->first));
  return q;
}

一旦此函数退出,客户端代码只能使用指向该对的 int 子对象的指针。但是由于 qp 之间的共享所有权,指针 q 使整个 Pair 对象保持事件状态。

一旦发生解除分配,必须将指向整个 Pair 对象的指针传递给删除器。因此,指向 Pair 对象的指针必须存储在删除器旁边的某个地方——换句话说,在控制 block 中。

对于一个不太人为的示例(可能更接近该功能的原始动机),请考虑指向基类的情况。像这样的:

struct Base1
{
  // :::
};

struct Base2
{
  // :::
};

struct Derived : Base1, Base2
{
 // :::
};

std::shared_ptr<Base2> creator()
{
  std::shared_ptr<Derived> p(new Derived());
  std::shared_ptr<Base2> q(p, static_cast<Base2*>(p.get()));
  return q;
}

当然,std::shared_ptr 的真正实现具有所有隐式转换,因此 p-and-qcreator 中跳舞不是必需的,但我将它保留在那里以类似于第一个示例。

关于c++ - 为什么在 std::shared_ptr 实现中需要两个指向托管对象的原始指针?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/34046070/

相关文章:

c - 如何在 BST 中添加节点?

c - C中的指针算术和 "generic"

c++ - <system_error> 类别和标准/系统错误代码

c++ - 为什么要使用指针而不是对象本身?

c++ - 在 C++ 中使用 system() 运行 2 个或更多 cmd 命令

c++ - 如何创建将坐标初始化为随机值的构造函数?

c++ - 过滤范围、lambda 和 is_sorted

c++ - 安全地重新定义 BOOST_FOREACH 宏

c++ - 找不到内存泄漏

c++ - 当我们使用右值引用时到底发生了什么,std::move 是如何工作的?