C++ 移动语义和内置类型
C++ move semantics and built-in types
我正在尝试使用以下代码来理解移动语义。
#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。我的问题是,如果我想劫持(而不是复制)class 的实例,例如这个 ,我该怎么办?假设我有 100 个内置类型的成员(而不是一个),使复制变得昂贵。
一个明显的方法是将对象存储在堆上并使用引用语义来传递它。但希望保留价值语义并且仍然能够规避复制。
不可能and/or没有意义。
我们从:
开始
A original; // original object
问题的主要部分是:
What do I do if I want to hijack (not copy) an instance of the class
such as this one?
所以这意味着我们最终得到:
A movedto; // new object that has all of original's members
但这里需要注意的是我们想要 "to circumvent the copying" 而我们不想使用引用或指针,即 "reference semantics",只有 "stack" 或 "value semantics" .
如果我们希望 movedto
在已经分配的相同内存位置拥有相同的成员,那么我们可以创建对 original
:
的引用
A& movedto{original}; // references members at the same memory locations.
但是这个问题的一部分说明我们没有使用引用,因为大概我们希望这个对象有不同的生命周期。因此,如果我们想保留 original
的成员 "alive" 并分配到当前块的末尾之外,那么我们会立即发现我们无法控制底层内存。
本题中original
是一个自动存储时间的对象。具有自动存储持续时间的对象会根据其范围自动管理其生命周期。编译器可能使用堆栈来存储它,并且编译器可能使用每次添加对象时向下移动的堆栈指针,但 C++ 标准没有指定应该如何完成。我们确实知道该标准指定具有自动存储持续时间的对象将按照范围结束时创建的相反顺序销毁。
因此,试图控制创建具有自动存储持续时间的对象的位置没有意义,将此类对象的成员分配给另一个对象也没有意义。内存是自动分配的。
如果我们想要重用已经分配为具有自动存储持续时间(stack/value 语义)的对象的一部分的变量,那么我们使用的内存将在该对象的生命周期结束时被释放。我们必须为此使用动态存储(即 "heap" 或 "reference semantics")。
我正在尝试使用以下代码来理解移动语义。
#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。我的问题是,如果我想劫持(而不是复制)class 的实例,例如这个 ,我该怎么办?假设我有 100 个内置类型的成员(而不是一个),使复制变得昂贵。
一个明显的方法是将对象存储在堆上并使用引用语义来传递它。但希望保留价值语义并且仍然能够规避复制。
不可能and/or没有意义。
我们从:
开始A original; // original object
问题的主要部分是:
What do I do if I want to hijack (not copy) an instance of the class such as this one?
所以这意味着我们最终得到:
A movedto; // new object that has all of original's members
但这里需要注意的是我们想要 "to circumvent the copying" 而我们不想使用引用或指针,即 "reference semantics",只有 "stack" 或 "value semantics" .
如果我们希望 movedto
在已经分配的相同内存位置拥有相同的成员,那么我们可以创建对 original
:
A& movedto{original}; // references members at the same memory locations.
但是这个问题的一部分说明我们没有使用引用,因为大概我们希望这个对象有不同的生命周期。因此,如果我们想保留 original
的成员 "alive" 并分配到当前块的末尾之外,那么我们会立即发现我们无法控制底层内存。
本题中original
是一个自动存储时间的对象。具有自动存储持续时间的对象会根据其范围自动管理其生命周期。编译器可能使用堆栈来存储它,并且编译器可能使用每次添加对象时向下移动的堆栈指针,但 C++ 标准没有指定应该如何完成。我们确实知道该标准指定具有自动存储持续时间的对象将按照范围结束时创建的相反顺序销毁。
因此,试图控制创建具有自动存储持续时间的对象的位置没有意义,将此类对象的成员分配给另一个对象也没有意义。内存是自动分配的。
如果我们想要重用已经分配为具有自动存储持续时间(stack/value 语义)的对象的一部分的变量,那么我们使用的内存将在该对象的生命周期结束时被释放。我们必须为此使用动态存储(即 "heap" 或 "reference semantics")。