我对以下移动堆栈对象的代码感到困惑:
int *pOuter = nullptr;
{
int j;
std::cout << "top of the inner stack=" << &j << std::endl;
int aData[] = {1,2,3,4,5}; // in a vanishing stack
aData[0] = 0; // to make sure that aData is on stack
int *pInner = aData;
std::cout << "Before Move: pOuter=" << pOuter << ", pInner=" << pInner << std::endl;
pOuter = std::move(pInner);
std::cout << "After Move: pOuter=" << pOuter << ", pInner=" << pInner << std::endl;
}
// aData[0] and pInner are no longer accessible
int i;
std::cout << "top of the outer stack=" << &i << std::endl;
int aCover[] = {10,9,8,7,6,5,4,3,2,1}; // to reclaim stack
std::cout << "aCover=" << aCover << ", pOuter=" << pOuter << ": ";
for (i = 0; i < 5; ++i) {
std::cout << *(pOuter + i) << ",";
}
std::cout << std::endl;
输出为:
top of the inner stack=0xb2125ff7cc
Before Move: pOuter=0, pInner=0xb2125ff7b0
After Move: pOuter=0xb2125ff7b0, pInner=0xb2125ff7b0
top of the outer stack=0xb2125ff804
aCover=0xb2125ff7d0, pOuter=0xb2125ff7b0: 0,2,3,4,5,
我很惊讶为什么它会起作用,因为堆栈对象 sData 应该被销毁并被 aCover 覆盖,这样 pOuter 应该会导致分段失败,或者至少指向 sCover。相反,aData 不会随内部堆栈一起销毁。
从显示的地址来看,我的猜测是C++编译器实际上在内部堆栈之前分配了以下堆栈对象:
int i;
int aCover[] = {10,9,8,7,6,5,4,3,2,1};
即便如此,在我看来,pOuter 是一个悬空引用,它的工作是偶然的。
我说得对吗?
正如评论中所指出的,
std::move
只是一种类型转换。那么它是如何工作的呢?
那么,我们以下面的代码为例:
Foo x;
Foo y = x;
这会在
x
中创建 y
的副本 - 即它调用 y
的复制构造函数来复制 x
。
而这个:
Foo x;
Foo y = std::move (x);
将
x
移至y
,如果您不再关心x
,则效率更高。
但是 - 这是关键 -
std::move
只有在 Foo
有合适的移动构造函数时才会做任何有用的事情,并且其签名是:
Foo &Foo (Foo &&other)
即参数
other
必须是 rvalue
,而 std::move
本质上是将 x
(以左值开始)转换为右值,从而告诉编译器调用移动构造函数而不是复制构造函数。
这就是std::move
所做的
全部。它本身不会移动任何东西。怎么可能呢?它不知道如何做,如果不存在合适的移动构造函数,编译器将回退到复制
x
。尤其是原始类型的情况。