通常将对象的引用计数存储在哪里?

时间:2019-06-20 19:18:05

标签: c++ smart-pointers

如果我们有一些智能指针类可以接受任意对象并提供引用计数的指针,那么我们如何实际存储对引用计数的整数?引用计数必须在指向同一对象的智能指针类的所有实例之间共享。

我想到的一个解决方案是将引用计数存储在我们指向的对象中,但这对于一般解决方案来说不是很好,因为每个对象都必须自己提供引用计数或从某些对象继承提供它的对象。

3 个答案:

答案 0 :(得分:3)

它通常存储在对象设计所需的任何位置。侵入式智能指针要求使用T来提供引用计数的存储。这就是使它们“具有侵入性”的原因。他们侵入了物体。

您概述的设计指定“可以采用任意对象”。因此,侵入式设计已无法实现。

由于智能指针的许多实例将不得不访问同一引用计数对象,因此该引用计数必须独立于任何一个实例。而且,由于它还必须独立于T,因此它必须是一个生命周期独立于T和引用它的智能指针实例的对象。

因此,智能指针在宣称拥有T的所有权之后,还必须创建引用计数对象来对其进行管理。通常,这是通过堆分配这样的对象来完成的。智能指针的副本还会获得一个指向引用计数的指针。

这也是为什么两个不同的std::shared_ptr构造函数声明对同一T*的所有权是非法的。您可以从已经拥有shared_ptr的{​​{1}}复制,但是不能仅仅将T*本身直接传递给构造函数。由于T*无法访问引用计数,因此T的构造函数不会知道别人拥有它,因此它将创建第二个引用计数块。

答案 1 :(得分:1)

std::shared_ptr使用的一般解决方案是分配一个单独的控制块,该控制块保存引用计数(以及其他内容,例如:析构函数,weak_ptr计数)。

(如果使用std::make_shared,则控制块和对象可以位于同一分配中)

您在第二段中描述的内容也存在,例如:在Boost中称为intrusive_ptr

答案 2 :(得分:0)

有多种存储引用计数的策略,具体取决于您要支持的操作。

这里的其他答案概述了一个选项,即在托管内存旁边分配一个辅助控制块,并使所有智能指针指向该辅助块。这样可以轻松快速地确定确切的引用计数,但是每个智能指针都需要额外分配,这可能会使速度变慢,并且在内存不足时可能会失败。 (当控制块与对象本身不连续存储时,还存在缓存友好性,它们可以查找控制块。)

另一个选择是引用链接,其中没有明确的引用计数。取而代之的是,您通过所有智能指针对一个循环的,双向链接的列表进行线程化。在向对象添加新的智能指针时,该智能指针会被拼接到链接列表中,而在删除智能指针时,会将智能指针从链接列表中拼接出来。这消除了对辅助分配的需求(除非您需要自定义删除器),并提高了局部性,但确定精确的引用计数却很昂贵。实际上需要精确的引用计数并不常见,因此这种权衡通常是一个合理的选择。