为什么 C++ 智能指针实现将引用计数器与指针对象一起保留在堆上?

问题描述 投票:0回答:4

阅读 Alexandrescu 和 wikipipidia 我看到指针和引用计数器存储在堆上。然后有人提到引用计数效率低下,因为计数器必须分配在堆上?为什么不存储在堆栈中?

c++ design-patterns boost smart-pointers c++-loki
4个回答
9
投票

因为一旦智能指针的当前实例超出范围,您就会丢失它。

智能指针用于模拟动态分配的自动存储对象。智能指针本身是自动管理的。因此,当一个物体被摧毁时,它存储在自动存储器中的任何东西也会被摧毁。但您不想丢失参考计数器。所以你将它存储在动态存储中。


3
投票

它不能存储在堆栈上,因为对象的副本也会导致引用计数的副本,这会违背其目的。


1
投票

正如其他人指出的那样,堆栈不是保存引用计数的合适位置,因为对象可能比当前堆栈帧的寿命更长(在这种情况下,引用计数将会消失!)

值得注意的是,通过将引用计数与对象本身“一起”存储,可以克服与将引用计数放在堆上相关的一些低效率问题。在 boost 中,这可以通过使用 boost::make_shared (对于共享指针)或 boost::intrusive_ptr 来完成。


1
投票

有不同类型的智能指针,设计用于不同的目的。您正在谈论的指针是一个共享智能指针

std::shared_ptr
),它有助于从多个位置共享对象所有权。
shared_ptr
的所有副本都会递增和递减相同的计数器变量,该变量放置在堆上,因为即使在第一个副本死亡后,它也需要可供
shared_ptr
的所有副本使用。

因此,

shared_ptr
内部保留了两个指针:指向对象和指向计数器。伪代码:

class SharedPointer<T> {
public:
// ...
private:
    T* obj;
    int* counter;
}

顺便说一句,当您使用

std::make_shared
创建对象时,实现可以通过分配足够的内存来保存计数器和对象,然后并排构建它们来优化分配。

这个技巧的极端为我们提供了一种侵入式引用计数模式:对象内部保存其计数器并提供

AddRef
Release
函数来递增和递减它。您可以使用侵入式智能指针,例如
boost::intrusive_ptr
,它使用这种机制,因此不需要分配另一个单独的计数器。这在分配方面更快,但需要将计数器注入受控类定义。

此外,当您不需要共享对象所有权而只需要控制其生命周期(以便在函数返回时被破坏)时,您可以使用作用域智能指针

std::unique_ptr
boost::scoped_ptr
。它完全不需要计数器,因为只有一份
unique_ptr
存在。

© www.soinside.com 2019 - 2024. All rights reserved.