【问题标题】:C++: custom deleter statusC++:自定义删除器状态
【发布时间】:2015-04-02 14:48:07
【问题描述】:

我声明了一个类似的类型

template <typename T>
using SmartPtr = std::unique_ptr<T, MyDeleter>;

在某些特定情况下,我想计算对对象的引用并在指针超出范围时有条件地删除它。

我知道有 std::shared_ptr 类,但我有一些我想通过公共接口访问的对象。其中一些对象归其类所有,其他类有一个工厂方法,可以产生它们创建的对象的所有权。

我的问题是:

  • SmartPtr 类型的每个指针是否都有自己的个人 MyDeleter 对象?
  • 是否有多个std::unique_ptrs 指向同一个对象被认为是一种不好的编码习惯?即使我使用自定义删除器?

在没有删除器的情况下使用 shared_ptr 对我来说感觉不对,因为类拥有的对象无论如何都不能被删除,因为它们不是通过 new 创建的。它们是数组的一部分,是类的成员。并非所有 SmartPtr 指向的对象都是使用 new 运算符创建的。

【问题讨论】:

  • “有多个 std::unique_ptrs 指向同一个对象” “unique”这个词是一个赠品,不是吗?你能进一步解释为什么std::shared_ptr 不适合你吗?
  • 这对我来说似乎很可笑。你认为你为什么需要这个?
  • 为什么不用升压精神?那么问题来了,你将拥有的所有空闲时间都找到一个用途。放弃这个有趣的指针想法,继续前进。
  • @LightningRacisinObrit 使用带有自定义删除器的 unique_ptr 滚动侵入式智能计数指针并不是无法使用的设计。它确实不必要地阻塞了复制构造,并且它给出了一个具有误导性的类型名称。我自己,我会写我自己的侵入式智能计数指针,但它会拥有一个 unique_ptr 而不是它内部的原始指针。那个 unique_ptr 会减少使用计数,所以基本上就是 OP 想要做的事情:我只是将它隐藏在一个层中,所以我得到了复制操作和一个更好的名字。
  • @biowep: shared_ptr 为您提供移动语义。引用计数从 1 到 2 再回到 1,而无需复制目标。

标签: c++ smart-pointers unique-ptr


【解决方案1】:

你想要一个指针成员,它可能拥有也可能不拥有它所指向的东西?简单,关注点分离:

std::unique_ptr<T> maybe_owning;
T* always_pointing;

constructor(const T& copy_and_own)
    :maybe_owning( new T(copy_and_own))
    ,always_pointing(maybe_owning.get())
{}
constructor(T* just_reference)
    :maybe_owning()
    ,always_pointing(just_reference)
{}
void do_task() {
   always_pointing->thing();
}

您有一个始终指向数据的指针,并且丝毫不关心所有权。
您还有一个可以有条件地用于拥有的智能指针。
所有问题都解决了,没有疯狂的代码。

我实际上经常使用这个,fstream 可能会或可能不会打开文件,istream&amp; 有时连接到 fstream,有时连接到 cin,然后我可以从任一来源读取一样。

【讨论】:

    【解决方案2】:

    这是一个有效的“也许删除,也许不是”类:

    enum class should_delete {
      yes,
      no,
    };
    struct maybe_delete {
      should_delete choice = should_delete::yes;
      template<class T>
      void operator()(T* t){
        if (choice!=should_delete::no)
          delete t;
      }
      maybe_delete() = default;
      maybe_delete(should_delete c):choice(c) {}
      maybe_delete(maybe_delete const&)=default;
      // move in terms of copy, then clear state to default:
      maybe_delete(maybe_delete&& o):maybe_delete(o){
        o.choice = should_delete::yes;
      }
    };
    template<class T>
    using maybe_unique_ptr = std::unique_ptr<T, maybe_delete>;
    

    是一种可能是唯一指针的类型。

    live exmaple

    使用{ptr, should_delete::no} 构造,以使指针不删除有问题的对象。这样maybe_unique_ptr 的(移动)副本将具有正确的状态。

    请注意,.reset(ptr) 可能很危险,因为该对象会获得之前存在的内容的删除状态。我通过小心的maybe_delete move ctor 确保了从 maybe_unique_ptrs 移动的状态与简单构造的状态相同。


    话虽如此,如果你想写一个侵入式智能指针,我不会把它作为一个原始的unique_ptr

    如果你看std::shared_ptr,他们有“上帝模式”构造函数:

    shared_ptr<T>::shared_ptr( T*, shared_ptr<U> )
    

    这让你有一个T 的shared-ptr,它将所有引用计数转发到一个完全不同的shared-ptr。这适用于“成员”shared-ptrs(以及其他用途)——如果你有一个结构 X 和一个成员 Y,你可以有一个 shared-ptr-to Y 实际引用计数封闭X 结构。

    对于侵入式智能指针,我会编写一个智能指针。它将存储一个T* 和一个“范围保护”类型的对象,用于进行递减的清理(范围保护甚至可以是一个unique_ptr 到一个引用计数接口,带有一个递减的销毁器)。为智能指针(几十行)做样板。在复制时,克隆范围保护和T*。在移动时,移动指针和范围保护(并清除源)。

    具有将原始T* 与空ref_count(如果需要)或T*ref_count 接口或两者兼而有之的构造函数。

    如果包含的对象有一个引用计数,我会很想使用它,只是为了及早检测关闭问题。


    bonus version with arbitrary deleter guarded by maybe_delete。这将允许您有条件地引用递减,例如,不会过多地混合这两个操作。使用空基类优化来防止空间浪费(std::default_delete 为空)。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2013-12-23
      • 1970-01-01
      • 1970-01-01
      • 2020-06-19
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-03-30
      相关资源
      最近更新 更多