这个是我的代码, 按照移动构造函数的定义, 我先创建了一个对象 a, 这个对象强制使用 move 调用移动构造函数赋值给 b, 这个时候, a, b 里面的成员变量的地址 data 应该是一样的, 为什么我这里显示的不一样?
运行结果
Constructor is called for 10 000000950D2FF7F8 // addr Move Constructor for 10 000000950D2FF818 // addr Destructor is called for 10 Destructor is called for nullptr
// C++ program with declaring the
// move constructor
#include <iostream>
#include <vector>
using namespace std;
// Move Class
class Move {
public:
int* data;
// Constructor
Move(int d)
{
// Declare object in the heap
data = new int;
*data = d;
cout << "Constructor is called for "
<< d << endl;
};
// Copy Constructor
Move(const Move& source)
: Move{ *source.data }
{
// Copying the data by making
// deep copy
cout << "Copy Constructor is called -"
<< "Deep copy for "
<< *source.data
<< endl;
}
// Move Constructor
Move(Move&& source)
: data{ source.data }
{
cout << "Move Constructor for "
<< *source.data << endl;
source.data = nullptr;
}
// Destructor
~Move()
{
if (data != nullptr)
// If pointer is not pointing
// to nullptr
cout << "Destructor is called for "
<< *data << endl;
else
// If pointer is pointing
// to nullptr
cout << "Destructor is called"
<< " for nullptr "
<< endl;
// Free up the memory assigned to
// The data member of the object
delete data;
}
};
// Driver Code
int main()
{
// Vector of Move Class
//vector<Move> vec;
//// Inserting Object of Move Class
//vec.push_back(Move{ 10 });
//vec.push_back(Move{ 20 });
Move a(10);
cout << &(a.data) << endl;
Move b = move(a);
cout << &(b.data) << endl;
return 0;
}
1
codehz 2022-06-19 22:43:24 +08:00
移动构造没有移动对象本体——移动的是内部引用的其他资源
(然后如果用 vector 的话,可以 vector<Move> vec; vec.emplace(10); 来直接原地构造到对应位置去 |
2
codehz 2022-06-19 22:44:46 +08:00
打错,是 emplace_back
|
3
stein42 2022-06-19 22:45:46 +08:00
相同的应该是 a.data 和 b.data ,它们的类型是 int*。
而不是 &(a.data) 和 &(b.data),它们的类型是 int**。 |
4
wevsty 2022-06-19 22:49:50 +08:00
&(a.data) 代表的是 a 这个对象中的 data 成员所在的地址。
所以为什么 &(a.data) 要等于 &(b.data)? 移动构造并不是把原先的对象取一个不同名字的引用,你实现的移动构造函数只能保证 a.data = b.data 但是 a 和 b 仍然是两个独立的对象。 |
5
woshichuanqilz OP @wevsty 那移动的意义在哪 这不和拷贝构造函数一样了吗?
|
6
woshichuanqilz OP @codehz 这个我理解是移动构造函数最常见的情况, 就是 move 一个临时变量, 但是如果我强制调用 move 在一个声明的变量上给另一个, 似乎没有出现移动构造的效果
|
7
woshichuanqilz OP @stein42 如果地址不相同的话说明复制了一份? 那么为什么叫移动, 不是和拷贝构造一样了
|
8
codehz 2022-06-19 23:10:25 +08:00
@woshichuanqilz
移动的意义就是可以表达不“深层次”复制内容的意图,而是转移原始对象的对引用资源到新的对象上(这个也得你自己实现,比如把原始对象里对应指针属性设置为 0 ,free 的时候就不会 double free 了) |
9
stein42 2022-06-19 23:16:29 +08:00
C++ 是把对象和资源关联到一起,移动是移动关联的资源。
这里 Move 对象关联的资源就是 new 分配的内存,析构函数会释放这块内存(如果不为空的话)。 复制构造函数是新分配了一块内存,并把值复制过来。 移动构造函数是转移了资源,这里既 a 分配的内存转移给了 b 。 |
10
yanqiyu 2022-06-19 23:23:27 +08:00 1
@woshichuanqilz 移动的意思是 *data 这个对象没必要被复制(重新分配空间,data = new int 以及赋值),并且原先的类失去所有权。
data 是个类成员,在不同类里面肯定是不同的东西,占据不同的地址 |
11
wevsty 2022-06-19 23:24:19 +08:00 1
@woshichuanqilz
通常约定,拷贝构造是对对象做深拷贝,移动构造尽量做移动(浅拷贝)。 对于基本数据类型( int ,float 等)深拷贝和浅拷贝是一样的。 但是对于指针类型,浅拷贝通常只复制指针保存的地址。光拷贝指针中保存的地址显然不能保证不会产生资源的冲突,所以如果做深拷贝需要重新分配一块空间再对指针中指向的数据进行再次拷贝。 另外移动构造还有转移所有权的含义,移动之后原对象应该被视为已经废弃的对象(或者说没有管理任何资源的对象)。 |
12
yanqiyu 2022-06-19 23:24:39 +08:00
要是 *data 这个对象复制开销很大,或者根本就不能复制,作用就来了
|
13
sora2blue 2022-06-20 00:47:02 +08:00
可不可以这样假设:类在底层实现的时候,对内置类型的成员直接存储,对不是内置类型的成员都只存储指针;内置类型如指针的拷贝花费较少不计入,在移动构造时对这两者浅拷贝即可。相对地,在拷贝构造时对内置成员一样拷贝,对不是内置类型的成员另外拷贝一遍指针指向的成员对象,然后存储这个新的指针值在底层,而这个就是移动构造的优化对象。
所以对类的成员取地址,取到的地址都是类底层实现时内置类型成员或非内置类型成员指针的地址,比如:OP 的例子里,指针都是内置类型,直接复制即可,a.data 和 b.data 的存储位置在底层仍然是不一样的;把 data 的类型换成结构体或者类,结果是一样的,在底层指向结构体和类的指针的存储位置仍然是不一样的。 |
14
Buges 2022-06-20 02:30:23 +08:00 via Android
move 对栈上的对象来说就是 copy ,对堆上的对象来说是 copy 指针。
|