【问题标题】:Why ref_count in shared_ptr implementation is int*为什么 shared_ptr 实现中的 ref_count 是 int*
【发布时间】:2017-08-29 03:38:56
【问题描述】:

我见过几个 shared_ptr 的实现,例如here。他们都将ref_count 声明为int*。我不明白如果它只是一个int,我们会失去什么。谢谢!

template <class T>
class shared_ptr {
    T* ptr;
    int* ref_count;

    /**
     * Initializes the ref count used for tracking the usage.
     */
    void initialize_ref_count() {
        if (ref_count != nullptr)
            return;
        try {
            ref_count = new int;
            *ref_count = 1;
        }
        catch (std::bad_alloc& e) {
            std::cerr << "Memory allocation error: " << e.what();
        }
    }
}

【问题讨论】:

  • 如果它只是一个int,它不会很共享。练习的重点是共享相同ptr 的多个shared_ptr 实例共享相应的引用计数。
  • @o11c 为什么?我认为这不是重点
  • @o11c 在超过 40 亿所有者之间共享同一个对象的实际代码中,您会发现很少。
  • @o11c 有符号数字到底是如何邪恶的?现实世界的代码创建了超过 2³² 的对象,这也是选择一个对象同样糟糕的更多原因。但是我不认为你应该自动引用计数 2³² 对象
  • 实际上是 unsigned 数字表现得“非常奇怪”。模 2^n 算法对于无符号值是唯一的,并且使它们主要适用于位字段,而不是实际整数。如果您可能需要符号位提供的额外容量,那么您几乎可以肯定只使用下一个更大的整数大小。

标签: c++ shared-ptr


【解决方案1】:

正如您在提供的实现 (in your link) 中所见,当共享指针是复制构造复制分配 时,指向引用计数器的指针 ( ref_count) 在管理同一指针的所有实例之间共享

    // Copy constructor
    shared_ptr(const shared_ptr& copy) {
        ptr = copy.ptr;
        ref_count = copy.ref_count; // see here
        if (ref_count != nullptr) {
            ++(*ref_count);
        }
    }

    // Assignment operator
    shared_ptr& operator=(const shared_ptr& copy) {
        ptr = copy.ptr;
        ref_count = copy.ref_count; // see here
        if (ref_count != nullptr) {
            ++(*ref_count);
        }
        return *this;
}

这样,该共享指针的所有实例,引用同一个内存位置来跟踪 ref 计数器,最后一个shared_ptr 将能够知道它是否需要这样做清理(​​delete分配的内存):

   ~shared_ptr() {
        --(*ref_count);
        if (*ref_count == 0) {
            delete ref_count;
            ref_count = nullptr;
            delete ptr;
            ptr = nullptr;
        }
    }

免责声明

为简单起见,此答案基于 OP 提供的示例。 shared_ptr 实现比示例中的实现要复杂得多(考虑原子性、竞争条件等......)。

【讨论】:

  • FWIW,operator= 函数有问题。在将 copy.ref_count 分配给它之前,它应该先减少 thisref_count
  • 感谢您的澄清。我不假设 OP 提供的实现是正确的。我唯一的目标是解释为什么需要指针。
  • 你说的复杂度只存在于std::shared_ptr,引用计数的概念不一定包括这个
【解决方案2】:

首先,要分享一些东西,你需要把它放在其他人可以访问的地方。正如@Igor Tandetnik 所指出的。因此,动态存储持续时间的对象可以很好地完成这项工作。具有动态初始化的静态存储持续时间的对象也可以这样做,但是该对象将在程序的其余部分存在,这是我们不希望的。


其次,shared_ptr 比这要复杂一些。典型的shared_ptr 指的是控制块。这个控制块通常包含:

  • 对象的共享引用数,
  • 有问题的对象或指向该对象的指针,
  • 以及对控制块的弱引用数。

为了线程安全,共享引用计数和弱引用计数通常由原子类型保存。

编辑:查看@Passer By 的评论。

【讨论】:

  • 如果你说的是std::shared_ptr,你忘记了类型擦除删除器
  • @PasserBy,哦...是的... :-)
猜你喜欢
  • 1970-01-01
  • 2016-05-30
  • 2016-04-10
  • 1970-01-01
  • 1970-01-01
  • 2012-11-23
  • 2020-11-19
  • 2016-02-26
  • 1970-01-01
相关资源
最近更新 更多