【问题标题】:std::unique_ptr and reset() or a Reset method for your class?std::unique_ptr 和 reset() 还是您班级的 Reset 方法?
【发布时间】:2015-04-14 23:21:03
【问题描述】:

使用std::unique_ptr::reset,您可以轻松地将您的实例恢复到新状态。

在 C++11 之前,为了实现类似的行为,我看到很多类定义了一个 Reset() 方法来重置其所有内部成员。但现在,我认为同样可以通过构造函数和析构函数以及resetunique_ptr 转换为类的新实例来完成。为什么您仍然更喜欢Reset() 方法,或者我们是否应该始终只使用unique_ptrreset 将它用于新实例,所以我想“重置”我们的类?

我能想到的唯一好处是您节省了有时可能很昂贵的分配/删除。 代价当然是维护 Reset() 函数并确保它与其他代码更改保持同步的复杂性。

是这样吗?这只是一个复杂性与性能的问题?

【问题讨论】:

  • 如果你想在栈上分配你的对象怎么办?

标签: c++ c++11 memory-management class-design unique-ptr


【解决方案1】:

这是一种非常好的通用方法,可以将您的类的状态重置为其默认构造状态:

// Previously constructed MyClass myvalue = ...
myvalue = MyClass{};

这涉及两个操作:

  1. 默认构造。
  2. 移动作业。

理想情况下,这两者都应该非常便宜。

【讨论】:

  • 三个操作?:1.默认构造。 2. 销毁myvalue。 3. 移动作业。
  • 我们能否以某种方式跳过移动赋值,让编译器就地破坏和构造类?我永远不会写这个,但是像MyClass::~MyClass(&myvalue); new (&myvalue) MyClass;
【解决方案2】:

分配/删除是一项成本,但通过指针访问对象的额外间接级别也有成本。例如,较差的数据局部性。

Reset() 函数也可以在只有引用或指针的上下文中调用。唯一应该知道对象由unique_ptr 管理的对象是所有者。在其他情况下,最好使用引用或指针。

但只要您知道成本并且只有所有者想要进行重置,我认为这样做没有任何问题。

当然,这在 C++11 中并不是什么新鲜事,在堆分配的对象上总是可以做到这一点。这只是unique_ptr 如何让生活更轻松的一个例子。

【讨论】:

    【解决方案3】:

    我能想到的唯一好处就是你节省了一个 分配/删除有时可能很昂贵

    不仅分配可能很昂贵,而且可能的内存碎片,糟糕的局部性(感谢@Cris在我脑海中搜索的术语),在频繁重置期间,最终会带来巨大的性能问题。

    unique_ptr 根本不是要重置您的实例状态。 unique_ptr::reset 重置unique_ptr所有权因此在“旧”实例上调用析构函数。

    所以这与您的实例 Reset 方法完全不同。

    重置时

    • 你没有实例化一个新的内存 -> pro

    • 可能可以重复使用相同的内存而不会丢弃它,因此请保持数据对齐和紧密打包(取决于如何您实现Reset 方法)-> 专业

    • 非安全内存管理,导致内存泄漏 -> 缺点

    如果是unique_ptr

    • 没有指针别名和内存管理控制-> pro
    • 每次分配一个新实例 -> 缺点

    所有proscons 自然是有争议的,取决于具体的实现和实际需求。

    【讨论】:

      【解决方案4】:

      我认为你在这里搞混了。

      unique_ptrshared_ptr 一样是表示所有权的构造。 使用unique_ptr 意味着您的类既不能被复制也不能被复制分配,除非您自己提供复制构造函数和复制赋值运算符。 不过,这将是额外的工作。

      因此,如果您仍想提供重置方法但不想手动重置每个成员变量的代码,您可以执行以下操作:

      MyClass::Reset()
      {
          this->~MyClass();
          new(this) MyClass();
      }
      

      这会将您的实例重置为其默认创建状态。

      这种方法的缺点是

      • 不保存异常,所以如果你的构造函数可以抛出你会有问题。
      • 您的成员在内部使用的动态内存将被丢弃,而不是直接重复使用。例如,vector 成员及其在堆上的关联内存被释放而不是被重用。

      其实你也可以实现Resetlike

      MyClass::Reset()
      {
          *this = MyClass();
      }
      

      虽然这可能比以前的解决方案慢。

      【讨论】:

        猜你喜欢
        • 2021-05-30
        • 1970-01-01
        • 2012-12-01
        • 1970-01-01
        • 2018-07-29
        • 1970-01-01
        • 2017-02-24
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多