std ::移动和构造/销毁对象

时间:2014-06-17 12:32:42

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

我记得,在任何函数调用之前,它会为堆栈中的函数结果和参数分配内存。 这是否意味着我有

T func()
{
    T a;
    return std::move(a);
}

我仍然会复制,因为已经为整个T分配了内存? 我也读过类似的问题

return a; 

相同
return std::move(a);

那么,我无法避免复制到堆栈? rvalue是否为堆栈值?

在某处使用它是否是好方法:

T a = std::move(func());

所以我会避免复制功能的结果?我是否还需要创建特殊的移动构造函数并移动operator =?

我试着测试它并得到了:

class Temp
{
public:
    Temp()
    {
        cout << "construct" << endl;
        i = 5;
    }
    ~Temp()
    {
        cout << "destruct" << endl;
    }
    Temp(const Temp& t)
    {
        i = t.i;
        cout << "copy construct" << endl;
    }
    Temp operator=(const Temp& t)
    {
        i = t.i;
        cout << "operator =" << endl;
        return *this;
    }
    int i;

};

Temp tempfunc1()
{
    Temp t1;
    t1.i = 7;
    return t1;
}

Temp tempfunc2()
{
    Temp t1;
    t1.i = 8;
    return std::move(t1);
}

int main()
{
        Temp t1;
        Temp t2;
        t2.i = 6;
        t1 = t2;
        cout << t1.i << endl;
        t1.i = 5;
        t1 = std::move(t2);
        cout << t1.i << endl;
        cout << "NEXT" << endl;
        t1 = tempfunc1();
        cout << t1.i << endl;
        cout << "NEXT" << endl;
        t1 = std::move(tempfunc1());
        cout << t1.i << endl;
        cout << "NEXT" << endl;
        t1 = tempfunc2();
        cout << t1.i << endl;
        cout << "NEXT" << endl;
        t1 = std::move(tempfunc2());
        cout << t1.i << endl;
}

结果:

construct
construct
operator =
copy construct
destruct
6
operator =
copy construct
destruct
6
NEXT
construct
copy construct
destruct
operator =
copy construct
destruct
destruct
7
NEXT
construct
copy construct
destruct
operator =
copy construct
destruct
destruct
7
NEXT
construct
copy construct
destruct
operator =
copy construct
destruct
destruct
8
NEXT
construct
copy construct
destruct
operator =
copy construct
destruct
destruct
8

好像没有使用std :: move。或者,如果存在特殊的构造函数和析构函数,它的工作只是

为什么“t1 = t2”同时调用复制构造函数和operator =?

请原谅我可能非常简单的大量问题,即使在阅读了很多相关内容之后,也许是因为我的英语不好,我仍然需要解释。

提前谢谢。

1 个答案:

答案 0 :(得分:3)

我对您的代码进行了一些更改,这可能有助于您了解和探索这一切是如何运作的。我在每个id对象中添加了一个Temp元素,以便更容易理解哪个对象是哪个,并且还更改了operator=的签名以返回引用而不是对象。首先,这是完成程序所需的include

#include <iostream>
using std::cout;
using std::endl;

接下来,这里的类现在包含std::move构造函数(带有&&)和move =运算符:

class Temp
{
    int id;
public:
    Temp() : id(++serial), i(5)
    {
        cout << "construct " << id << endl;
    }
    ~Temp()
    {
        cout << "destruct " << id << endl;
    }
    Temp(const Temp& t) : id(++serial), i(t.i)
    {
        cout << "copy construct " << id << " from " << t.id << endl;
    }
    Temp(Temp &&t) : id(++serial), i(t.i)
    {
        t.i = 5;  // set source to a default state
        cout << "move construct " << id << " from " << t.id << endl;
    }
    Temp &operator=(const Temp& t)
    {
        i = t.i;
        cout << "operator = " << id << " from " << t.id << endl;
        return *this;
    }
    Temp &operator=(Temp&& t)
    {
        i = t.i;
        t.i = 5;  // set source to a default state
        cout << "move operator = " << id << " from " << t.id << endl;
        return *this;
    }
    int i;
    static int serial;
};


int Temp::serial = 0;

您的功能仍然相同,但请参阅评论

Temp tempfunc1()
{
    Temp t1;
    t1.i = 7;
    return t1;
}

Temp tempfunc2()
{
    Temp t1;
    t1.i = 8;
    return std::move(t1);   // not necessary to call std::move here
}

我略微改变了main()以显示这一切是如何运作的:

int main()
{
    Temp t1;   
    Temp t2;   
    t2.i = 6;   
    t1 = t2;   
    cout << t1.i << endl;
    t1.i = 5;
    t1 = t2;
    cout << t1.i << endl;
    cout << "NEXT" << endl;
    t1 = tempfunc1();
    cout << t1.i << endl;
    cout << "NEXT" << endl;
    t1 = std::move(tempfunc1());
    cout << t1.i << endl;
    cout << "NEXT" << endl;
    t1 = tempfunc2();
    cout << t1.i << endl;
    cout << "NEXT" << endl;
    Temp t3(tempfunc1());
    cout << t3.i << endl;
    cout << "NEXT" << endl;
    Temp t4(t1);
    cout << t4.i << endl;
}

最后是输出:

construct 1
construct 2
operator = 1 from 2
6
operator = 1 from 2
6
NEXT
construct 3
move operator = 1 from 3
destruct 3
7
NEXT
construct 4
move operator = 1 from 4
destruct 4
7
NEXT
construct 5
move construct 6 from 5
destruct 5
move operator = 1 from 6
destruct 6
8
NEXT
construct 7
7
NEXT
copy construct 8 from 1
8
destruct 8
destruct 7
destruct 2
destruct 1

如您所见,使用固定的operator=,不会创建临时值。此外,一旦提供了operator=的移动版本,临时对象(与您的tempfunc1()tempfunc2()函数返回的对象一样)会自动使用移动语义。您的tempfunc2()确实不需要std::move内的t3电话。正如你所看到的那样,这仅仅创造了另一种临时性,所以它的伤害超过了它的帮助。最后请注意,在创建move时,只创建了一个对象,并且不需要临时或operator=构造函数。

更新

可能值得注意的是,移动构造函数在这个简单的类中并没有多大帮助,但它对于使用已分配内存或计算成本昂贵的类有很大帮助。在这些情况下,记住移动构造函数(或nullptr的移动版本)中需要多个步骤是有用的。具体来说,您必须:

  1. 释放目标对象使用的所有资源,然后
  2. 将资源从源对象移动到目标对象,然后
  3. 设置源对象状态,以便可以调用析构函数(例如,如果类已分配内存,则应将指针设置为*this,以便析构函数将正常运行),然后最后,< / LI>
  4. 返回{{1}}。