【问题标题】:object destructed before move operation移动操作前销毁的对象
【发布时间】:2015-09-01 16:25:36
【问题描述】:
#include <iostream>
static int i=0;
using namespace std;
class Movable
{
public:
    Movable ():mId(++i){
        cout<<"constructing it "<<mId<<endl;
    };
    Movable (const Movable&)=delete;
    Movable (Movable&)=delete;
    void operator=(Movable&)=delete;
    void operator=(const Movable&)=delete;

    Movable (const Movable&& aObject)
    {
        cout<<"Moving it constant "<<mId<<endl;
//      mId=++i;
    };
    Movable (Movable&&aObject)
    {
        cout<<"Moving it "<<mId<<endl;
    };
    Movable &operator=( Movable&&aObject)
    {
        cout<<"Moving it assignment "<<mId<<endl;
        return *this;

    }
    Movable &operator=(const Movable&&aObject)
    {
        cout<<"Moving it assignment constant "<<mId<<endl;
        return *this;

    }
    ~Movable ()
    {
        cout<<"destroying it "<<mId<<endl;
    }

    int getId() const {
        return mId;
    }

private:
    int mId;
};

Movable&& CreatenNewMovable ()
{
    Movable lM;
    return std::move(lM);
}

int main() {
    Movable a;

    a=CreatenNewMovable();

    return 0;
}

这段代码的输出结果是

constructing it 1
constructing it 2
destroying it 2
Moving it assignment 1
destroying it 1

我有点困惑如何销毁临时对象然后将其移至第二个。这是未定义的行为吗?我错过了有关移动操作的一些内容?

【问题讨论】:

    标签: c++11


    【解决方案1】:

    是的,这个功能

    Movable&& CreatenNewMovable ()
    {
        Movable lM;
        return std::move(lM);
    }
    

    坏了。

    右值引用仍然是一个引用,在这种情况下,它是对本地堆栈对象的引用,该对象在函数终止时(在它被移出之前)被销毁。相反,您应该只按值返回它,它仍然会被移出,如果有复制省略,那么它将是有效的。

    【讨论】:

    • 我改了函数 Movable CreatenNewMovable () { Movable lM;返回 lM; } 输出现在正在构造它 1 构造它 2 移动它 赋值 1 破坏它 2 破坏它 1. 这意味着对象被移动然后被破坏。这与复制操作有什么不同?
    • 对于所讨论的类,只有在移动比复制更有效时才会有所不同。在例如的情况下一个 std::vector,移动操作效率更高,因为它不分配新内存,也不复制或移动所有成员对象——移动通过“窃取”对象所拥有的指针来工作搬家了。
    【解决方案2】:

    看看这个sn-p:

    Movable&& CreatenNewMovable ()
    {
        Movable lM;
        return std::move(lM);
    }
    

    其实这是未定义的行为。有两个问题:

    1. 函数只能按值(可能是参考值)返回其值。因此,您必须在此处将Movable&amp;&amp; 替换为Movable
    2. 在堆栈上创建Movable lM; 并在函数外部引用它是UB。当函数退出时,对象不再存在。只需按值返回它 - 在您的情况下,复制省略将生效。

    最后,一个有效的方法是:

    Movable CreatenNewMovable ()
    {
        Movable lM;
        return lM;
        // ..or..
        // Even better to return like this(copy elision has less chances to fail)
        // return Movable();
    }
    

    这将产生您正在等待的结果:

    $ ./w 
    constructing it 1
    constructing it 2
    Moving it assignment 1
    destroying it 2
    destroying it 1
    

    您甚至可以通过删除 main 中的空对象创建来减少这项工作:

    int main() {
        Movable a=CreatenNewMovable();
        return 0;
    }
    /* Results:
    $ ./w
    constructing it 1
    destroying it 1
    */
    

    【讨论】:

      猜你喜欢
      • 2010-12-31
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-10-20
      • 1970-01-01
      • 2019-11-10
      • 2017-01-11
      相关资源
      最近更新 更多