【问题标题】:Why unique_ptr with custom deleter won't work for nullptr, while shared_ptr does?为什么带有自定义删除器的 unique_ptr 不适用于 nullptr,而 shared_ptr 可以?
【发布时间】:2018-10-16 12:54:00
【问题描述】:

使用unique_ptrshared_ptr 作为范围保护的简单代码。所有关于要清除的信息都在deleter 中捕获,所以我认为使用nullptr 作为构造函数是安全的。

显然,在 Visual C++ 2017 (14.1) 中,unique_ptr 无法正常工作,但shared_ptr 可以正常工作。这是微软的怪癖,还是标准阻止在持有nullptr 时调用unique_ptrdeleter

在下面的代码中,我不得不用(void*)1 构造一个unique_ptr。如果我用nullptr 构造它,cleaner 将不会被调用。对于shared_ptr,没有区别,总是调用cleaner

#include <memory>
#include <iostream>

int main()
{
    int ttt = 77;

    auto cleaner = [&ttt](void*) {
        std::cout << "cleaner: " << ttt << "\n"; // do something with capture here instead of print
    };

    std::unique_ptr<void, decltype(cleaner)> p((void*)1, cleaner);

    std::shared_ptr<void> q(nullptr, [&ttt](void*) {
        std::cout << "shared: " << ttt << "\n"; // do something with capture here instead of print
    });

    std::cout << "done\n";
    return 0;
}

【问题讨论】:

  • @KillzoneKid 如我所说,请将(void*)1 替换为nullptr 并重试。
  • 不需要用(void*)1构造一个无效的指针,用&amp;p就行了。
  • 可能值得一看 gsl::finally,它是 Guideline Support Library 的一部分,它正在实施来自 CppCoreGuidelines (Stroustrup & Sutter) 的建议。
  • @Barry:标准并不是这么说的。 shared_ptr 默认构造函数将创建一个空的 shared_ptr。复制或移动一个空的 shared_ptr 也是如此,并且移动会使旧的 shared_ptr 为空。但它并不是说用nullptr 构造会导致空的shared_ptr;相反,它保证use_count() == 1

标签: c++ c++11 visual-c++ language-lawyer


【解决方案1】:

unique_ptr 的析构函数需要这样做:

23.11.1.2.2 unique_ptr 析构函数 [unique.ptr.single.dtor]

2 效果:如果get() == nullptr 没有效果。否则get_deleter()(get())

其实shared_ptr的析构函数也需要这样做:

23.11.2.2.2 shared_ptr 析构函数 [util.smartptr.shared.dest]

— (1.1) 如果*this 为空或与另一个shared_ptr 实例(use_count() &gt; 1)共享所有权,则没有副作用。

— (1.2) 否则,如果*this 拥有一个对象 p 和一个删除器 d,则调用 d(p)。

因此在传递空指针的同时依赖智能指针在范围退出时执行任意操作是不可靠的。

【讨论】:

  • 这需要我编写自己的包装器(想使用 unique_ptr)来包装一些 MS-Window 的句柄。其中 -1 用于无效句柄,0 是需要清理的有效句柄。遗憾的是,在 nullptr 上调用 delete 被定义为无论如何都没有效果。
  • 是的,很遗憾 nullptr no-op 是 unique_ptr 的一部分——它应该是默认删除器的一部分。
  • 不,shared_ptr 确实调用了删除器。
  • @BenVoigt 实际上都不成立。您可以构造一个空的shared_ptr,其get() 返回一个非空指针(通过别名构造函数)。
  • 请注意,我使用帮助程序解决了“unique_ptr 中的 Win32 句柄”问题,该帮助程序确保将 INVALID_HANDLE_VALUE 转换为 0,因此我最终得到 ScopedHandle hFile(safe_handle(CreateFile2(...)))。有关示例用法,请参阅 scoped.hDirectXTex
猜你喜欢
  • 2015-09-11
  • 1970-01-01
  • 2015-02-04
  • 1970-01-01
  • 2012-04-11
  • 1970-01-01
  • 2016-11-23
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多