【问题标题】:Move constructor/operator=移动构造函数/操作符=
【发布时间】:2011-03-08 20:54:12
【问题描述】:

我正在尝试了解 C++ 的新功能,即移动构造函数和赋值X::operator=(X&&),我发现了interesting example,但唯一我什至不明白但更不同意的是移动 ctor 和赋值运算符中的一行(标记在下面的代码中):

MemoryBlock(MemoryBlock&& other)
   : _data(NULL)
   , _length(0)
{
   std::cout << "In MemoryBlock(MemoryBlock&&). length = " 
             << other._length << ". Moving resource." << std::endl;

   // Copy the data pointer and its length from the 
   // source object.
   _data = other._data;
   _length = other._length;

   // Release the data pointer from the source object so that
   // the destructor does not free the memory multiple times.
   other._data = NULL;
   other._length = 0;//WHY WOULD I EVEN BOTHER TO SET IT TO ZERO? IT DOESN'T MATTER IF IT'S ZERO OR ANYTHING ELSE IT IS JUST A VALUE.
}

所以我的问题是:我必须将 lenght_ 的值设置为零还是可以保持不变?不会有任何内存泄漏,也不会少一个表情。

【问题讨论】:

  • 也许this answer 也有帮助...
  • 我希望更正一些小细节。首先,由于此构造函数正在初始化 _data 和 _length 然后复制到它们,它应该只使用正确的值进行初始化。我不会说other.data = NULL 是“释放”,它更像是“取消设置”,没有释放内存。另外,你应该使用'nullptr'而不是'NULL',大致相同,但'nullptr'是正确的 C++。还建议避免使用std::endl,它会使您的程序在等待刷新输出时停止。除非您需要该功能,否则首选"\n"

标签: c++ c++11 constructor move-semantics move-constructor


【解决方案1】:

因为“移出”对象最终仍会被破坏,所以你必须让它保持一致的状态。当然,具体如何执行此操作取决于您的对象,在这种情况下,这显然意味着清空数据指针并将长度设置为零。

【讨论】:

  • @Terry 但是如果有零或千为什么会有所不同?它仍然是有效的 int。
  • @A-ha 有效值与正确/一致值之间存在差异。
  • 令人难以置信的是人们会支持正确的答案。多么傲慢。
  • @Terry 经过一番思考,你是对的。我接受你的回答。
【解决方案2】:

显然,程序员决定长度应该总是有一个正确的值。如果不设置,析构函数中的代码将不再打印正确的东西:

std::cout << "In ~MemoryBlock(). length = "
                << _length << ".";

【讨论】:

  • 你不明白我的问题是什么。
  • 你错了。我是否分配零都没有关系。 dtor 中的值仍然会被打印出来。
  • @A-ha: 如果你不设置 other._length 为0,它在析构函数中仍然输出为0
  • @Bill 但在 dtor 中,它会打印在 ctor 中设置的值(无论是否将 lengt_ 设置为零)。
【解决方案3】:

这个问题很难得到明确的答案。移动语义是新的,C++ 社区仍在学习如何正确使用它。

我看到的一个合理规则是“移离”对象必须安全地销毁并且能够被分配一个新值。不必将_length 设置为零来满足该规则,坦率地说,我不确定用 int 表示无效状态的好值是多少;在你的情况下可能是 -1?

【讨论】:

    【解决方案4】:

    您从中移动数据的对象可能不是临时对象(例如,您可以使用 std::move) - 让您的对象处于无效状态是一种不好的形式。

    【讨论】:

    • 所以重置它的值对你来说是可以接受的吗?有趣,有趣...
    • 是的,因为这就是移动构造函数的用途。您正在将一个对象内容的所有权转移给另一个对象。
    【解决方案5】:

    _length_data 是语义相关的项目。对于处于一致状态的对象,_length 应该始终告诉您_data 指向的块中有多少内存。当_data 指向100 个块时,_length 应该是100。当_data 指向1 个块时,_length 应该是1。如果_data 没有指向任何东西(NULL),那么_length应该为 0。否则,如果 _data 为 NULL,_length 为 100,则您的对象处于不一致状态。当我这样做时:

    for (int i = 0; i < _length; ++i)
    {
      // do something with _data[i], such as:
      _data[i] = 0;
    }
    

    我不应该崩溃。如果你没有正确设置_length,它会崩溃。 真正的问题是,为什么要故意让对象处于不一致的状态而导致崩溃?

    【讨论】:

    • 但在这种情况下,您已经在上面的行中分配了正确的长度_。我所说的一行所做的只是使 tmp 对象无效,所以我再说一遍:不,在我看来,length_ 的值是什么并不重要。
    • 在无效对象中继续。
    • @A-ha: other._length 只设置在一个地方:other._length = 0; 从逻辑上讲,您的意见是让对象处于不一致状态是可以的。我不同意。
    • 但是在不改变 length_ 的情况下,它的状态有什么不一致的地方?没有。而且我想得越多,我就越相信即使是 other._data = NULL 这一行也只是为了安全起见(只是为了让指针指向“无”)。
    • @A-ha:如果析构函数除了调用 delete 之外还必须对内存做一些事情(例如对存储在块中的对象调用析构函数),那么如果 _length 不是,您的代码将崩溃正确设置。在这个 specific 实例中,析构函数只调用delete,而忽略_length 的值。然而,指望代码永不改变是潜在错误的重要来源。 (例如,将for 循环放在我的析构函数中的答案中并观察段错误。)
    【解决方案6】:

    将值设置为零只会暴露一致性。但是,答案实际上取决于打算如何使用 _length 来销毁对象。如果没有使用长度来确定是否应该删除 _data(当移动的对象超出范围时),那么我们可以说在移动对象时忽略 _length 是安全的。只需确保在任何派生类型中保持这种一致性,并确保记录您忽略 _length 的决定。

    【讨论】:

      猜你喜欢
      • 2013-09-04
      • 2011-05-22
      • 1970-01-01
      • 2016-05-19
      • 2018-09-23
      • 2014-11-22
      • 1970-01-01
      • 2017-11-03
      • 1970-01-01
      相关资源
      最近更新 更多