【问题标题】:can I call destructor in move assignment operator?我可以在移动赋值运算符中调用析构函数吗?
【发布时间】:2015-09-26 11:34:04
【问题描述】:

在移动赋值运算符中调用 d-tor 是一种好习惯吗?

这里有一些示例代码:

VectorList &operator = (VectorList &&other){
    ~VectorList(); // if this is not a good practice,
                   // I will need to paste whole d-tor here.

    _buffer     = std::move(other._buffer       );
    _dataCount  = std::move(other._dataCount    );
    _dataSize   = std::move(other._dataSize     );

    other._clear();

    return *this;
}

我应该使用这段代码,还是应该使用带有移动构造对象的 swap()?

【问题讨论】:

  • 您是否考虑过创建一个函数来“清理”对象并从析构函数和移动赋值运算符中调用该函数?
  • 析构函数通常不负责将对象返回到初始化状态,所以这样做我会感到不舒服。为什么不打电话给this->clear()
  • @Nick 作为旁注,我希望你知道std::move 不会“移动”任何东西,它只是对右值引用的强制转换。如果_buffer 是一个原始指针,那么_buffer = other._buffer; 就足够了。
  • 是的,但斯科特迈耶斯“告诉”我你应该用移动来做,因为更好的风格和表现出意图。 std::move 是一个简单的演员表...

标签: c++ c++11 dry move-assignment-operator


【解决方案1】:

~VectorList 不仅仅是运行 dtor 主体中的代码:它实际上破坏了对象。

之后,该存储空间将不再使用。您可以使用构造函数在那里构造一个新对象,但简单地访问成员将是未定义的行为,或者需要语言律师找到让它被定义的漏洞。

即使已定义,它也是危险的,因为在销毁自动存储对象时抛出的异常是坏消息。另外,如果分配给的类实际上是派生类型,则 dtor 调用本身就是 UB!

这两种方法都不值得。收益太小了。

更好的选择是复制交换(这至少很容易纠正:它可以防止一些优化),或者从 dtor 和赋值中重构出“清晰”的代码。然后在两个地方都调用它。

【讨论】:

  • “当自动存储对象被销毁时抛出异常是个坏消息”——谁在这样做?从析构函数中抛出几乎从来都不是一个好主意,除非没有办法,但无论如何都会崩溃。如果有人这样做,您也可能会遇到破坏问题,所以我认为防御这一点不是很重要的考虑因素。
  • @voidstar 不是“在销毁期间”,而是“在被销毁时”——在(希望随后的)ctor 完成之前的任何时间。在对象的重建过程中,如果抛出异常,您最终会得到一个未构造的对象。如果在自动存储中,它将被双重销毁。
  • 我明白你的意思,但是答案文本中的语法有歧义,听起来你害怕抛出析构函数。相反,您的意思是真正的风险是赋值运算符代码中的抛出操作。顺便说一句,在赋值运算符代码期间不考虑异常的影响是不安全的,因为其他原因......不匹配的字段状态也可能同样糟糕,比如错误的缓冲区长度。
  • 手动调用析构函数除了运行该函数的代码之外没有任何作用。删除运算符不会以某种方式被使用,对象也不会因过早超出范围而被释放。在销毁使用placement new 构造的对象时,通常会看到显式的析构函数调用。
  • @chris 不,你错了。我对delete 或解除分配只字未提,所以那部分是正确的,但无关紧要,因为它不同意我没有说的事情。调用析构函数不仅仅是运行析构函数的主体:它会破坏对象,结束其生命周期。存储仍然存在,但不再包含已销毁类型的对象。指向该存储的指针的有效使用现在受到极大限制,直到新对象的生命周期在该存储中开始。与 dtor 具有相同主体的函数不会这样做
【解决方案2】:

Scott Meyers 说不要使用 swap(): http://scottmeyers.blogspot.sg/2014/06/the-drawbacks-of-implementing-move.html

关于您当前的实现,您似乎可以更简单地做到这一点。我想析构函数实际上删除了_buffer 并且几乎没有做其他事情。如果这是真的,你应该用 delete _buffer 替换你更难推理的显式析构函数调用。

【讨论】:

  • @nick swap 的实现比上面的要好。
  • @Nick Scott Meyers 的观​​点相当无语:时间并没有在三指针分配中丢失,而是在 delete 调用中丢失。如果在高速缓存中,分配需要大约五个周期,new/delete 对在 250 个周期的范围内。此外,temp = a; a = b; b = temp; delete b;delete a; a = b; b = nullptr; 之间甚至没有性能差异:两个版本都将ab 加载到寄存器中,然后将值存储到ab。 Scott Meyers 在撰写本文时显然被 a = b 的抽象概念蒙蔽了双眼。
猜你喜欢
  • 2020-04-10
  • 2016-05-19
  • 2020-12-03
  • 2013-06-11
  • 2011-03-13
  • 2017-05-15
  • 2014-11-27
  • 2017-06-12
  • 2020-09-06
相关资源
最近更新 更多