【问题标题】:C++ primer 5th edition: Difference between the deleter of a shared_ptr and of unique_ptr'sC++ 入门第 5 版:shared_ptr 和 unique_ptr 的删除器之间的区别
【发布时间】:2020-11-16 21:22:17
【问题描述】:

在《C++ Primer, 5th Edition》中,据说一个shared_ptr的deleter的类型直到运行时才知道,因为deleter不是直接作为成员存储的,而是作为一个可以指向的指针到删除器。 unique_ptr 中删除器的类型在编译时是已知的,因为它是 unique_ptr 本身的一部分。

所以,我做了这个例子:

#include <functional>

template <typename T>
struct SharedPtr
{
    void(*pDel_)(T*) = nullptr;
    T* ptr_{ new T{} };
    ~SharedPtr(){ pDel_ ? pDel_(ptr_) : delete ptr_; }
};

template <typename T, typename D = std::function<void(T*)>>
struct UniquePtr
{
    D pDel_;
    T* ptr_{ nullptr };
    UniquePtr(T* p = nullptr, D del = D{}) :
        ptr_(p), pDel_(del){}
    ~UniquePtr(){ pDel_(ptr_); }
};

int main()
{
    SharedPtr<int> spi{};
    cout << *spi.ptr_ << endl;
    UniquePtr<std::string> upd{new std::string("Helo!"),
        [](std::string* p){std::cout << "freeing memory...\n"; delete p; }};

}
  • 在我看来,SharedPtr 中删除器的类型在编译时 (void(*)(T*)) 是已知的,但值不是。

  • 另一方面,UniquePtr 中删除器的类型在编译时也确实是已知的,但值可能不知道。

  • 所以书上说:

// del bound at compile time; direct call to the deleter is instantiated
del(p);   // no run-time overhead
The type of del is either the default deleter type or a user-supplied type. It doesn’t
matter; either way the code that will be executed is known at compile time. Indeed, if
the deleter is something like our DebugDelete class (§ 16.1.4, p. 672) this call might
even be inlined at compile time.
By binding the deleter at compile time, unique_ptr avoids the run-time cost of an
indirect call to its deleter. By binding the deleter at run time, shared_ptr makes it
easier for users to override the deleter.

如果UniquePtr 中的删除器是指向函数的指针,但该指针是nullptr,则del(p) 未定义。

请帮助我理解这一段。我已经实现了我的shared_ptrunique_ptr 只是为了练习。

【问题讨论】:

    标签: c++ shared-ptr unique-ptr


    【解决方案1】:

    在我看来,SharedPtr 中删除器的类型在编译时是已知的 (void(*)(T*)),但值不是。

    您的 SharedPtr 也是如此。

    std::shared_ptr 不是这样。

    另一方面,UniquePtr 中删除器的类型在编译时也确实是已知的,但值可能不知道。

    当您使用std::function&lt;void(T*)&gt; 实例化 UniquePtr 时,您只会在编译时知道该类型,而不知道由它包装的函数对象的类型。

    std::unique_ptr 默认不使用 std::function 删除器。

    如果 UniquePtr 中的删除器是指向函数的指针,但该指针为 nullptr,则 del(p) 未定义。

    这是真的。不要那样做。

    请帮助我理解这一段。

    仅仅因为一个唯一的指针在编译时可以有一个已知的删除器,并不意味着它必须有这样的删除器。函数包装器既不是函数指针也不是编译时删除器,因为它们具有运行时状态。

    使用无状态删除器,例如 std::default_delete&lt;T&gt;(或者引用中提到的可能也是无状态的 DebugDelete)来获得编译时间优势。

    【讨论】:

    • 你的意思是,如果我这样做:std::unique_ptr&lt;int, std::function&lt;void(int*)&gt;&gt; upi(new int{7}, [](int* p ){delete p;});std::shared_ptr 的运行时开销相同?
    • @Maestro 关于删除器的编译时间:基本上是的。除了原子引用计数器等之外,共享指针还有其他开销。
    • 好的。太感谢了。你会推荐一些好的参考来阅读智能指针是如何实现的吗?
    • @Maestro 我建议阅读有关类型擦除的内容。它被std::functionstd::shared_ptr 应用于删除器。
    • @Maestro 您可以阅读源代码以获取详细信息。 Boost 文档可能有话要说,尽管它可能主要是关于如何使用它们。
    猜你喜欢
    • 1970-01-01
    • 2011-10-16
    • 1970-01-01
    • 2013-08-24
    • 1970-01-01
    • 2021-09-04
    • 2020-02-22
    • 1970-01-01
    • 2014-10-12
    相关资源
    最近更新 更多