我正在尝试使用以下代码来理解移动语义。
#include <iostream>
#include <utility>
using namespace std;
class A
{
public:
int a;
A():a{1}{}
A(A&& rref):a{rref.a}{cout<<"move constructor called"<<endl;}
A(const A& ref):a{ref.a}{cout<<"copy constructor"<<endl;}
};
int main(){
A original; // original object
cout<<"original.a = "<<original.a<< "| address original.a="<< &(original.a)<<endl;
A movedto (std::move(original)); // calls A(A&&)
cout<<"original.a = "<<original.a<< "| address original.a"<< &(original.a)<<endl;
cout<<"movedto.a = "<<movedto.a<<"| address movedto.a"<< &(movedto.a)<<endl;
return 0;
}
其中提供以下输出
original.a = 1| address original.a=0x7fff1611b6d0
move constructor called
original.a = 1| address original.a0x7fff1611b6d0
movedto.a = 1| address movedto.a0x7fff1611b6e0
可以看出,original.a
和movedto.a
的地址不同,因此成员a
在A(A&& rref):a{rref.a}
中进行了复制操作。
我知道 move 会降级为内置类型的 copy 。我的问题是如果我想劫持(不复制)类的实例(如此),该怎么办?假设我有100个内置类型的成员(而不是一个),使复制变得昂贵。
一种显而易见的方法是将对象存储在堆上并使用引用语义来传递它。但是希望保持价值语义,并且仍然能够规避复制。
答案 0 :(得分:1)
这是不可能的和/或没有意义。
我们从:
开始A original; // original object
问题的主要部分是:
如果我想劫持(不复制)该类的实例,该怎么办? 比如这个?
这意味着我们最终得到:
A movedto; // new object that has all of original's members
但需要注意的是,我们希望“绕过复制”,我们不想使用引用或指针,即“引用语义”,只需要“堆栈”或“值语义”。
如果我们希望movedto
在已分配的相同内存位置拥有相同的成员,那么我们只需创建对original
的引用:
A& movedto{original}; // references members at the same memory locations.
但是这个问题的一部分表明我们没有使用引用,因为我们可能希望这个对象具有不同的生命周期。因此,如果我们想让original
的成员“活着”并分配到当前块的末尾,那么我们立即发现我们无法控制底层内存。
在这个问题中,original
是一个具有自动存储功能的对象。具有自动存储持续时间的对象根据其范围自动管理其生命周期。编译器可能已经使用堆栈来存储它,并且编译器可能使用每次添加对象时向下移动的堆栈指针,但C ++标准没有指定应该如何完成。我们知道该标准指定具有自动存储持续时间的对象将按照范围结束时创建的相反顺序销毁。
因此,尝试控制创建具有自动存储durtaion的对象的位置没有意义,并且将这样的对象的成员分配给另一个也没有意义。内存自动分配。
如果我们想重用已经作为具有自动存储持续时间(堆栈/值语义)的对象的一部分分配的变量,那么我们将使用当该对象的生命周期结束时将被释放的内存。我们必须使用动态存储(即“堆”或“引用语义”)。