2017-10-08 38 views
1

我想我理解堆栈是如何工作的,当变量被移动时会发生什么,但是我找不到这个问题的答案。让我解释一下:将变量移动到外部作用域时堆栈会发生什么变化?

当一个新的作用域被输入/创建时,会在堆栈顶部获取一定量的内存。堆栈指针指向这个内存。它表示堆栈的当前大小。当范围被留下时,通过使堆栈指针返回到前一个位置来释放内存。

在C++ 11中移动语义或更高版本将一些数据的所有权从一个变量移动到另一个变量。这避免了复制数据,因为保存数据的内存保持不变。移动之后,移动到的变量指向数据在内存中的位置,移动的变量基本上变成空指针。在这里,我可能会犯第一个错误,将移动语义与指针过于紧密地联系起来。我是吗?

实际问题: 变量在内部作用域中创建,然后移动到外部作用域中的变量。然后内部范围退出。堆栈会发生什么?

鉴于上述情况,堆栈指针应该返回到它的前一个位置并释放内部作用域内存。但是它不能这样做,因为内部范围的内存仍然有效,因为它现在附加到外部范围变量。在外部(可能是全局的)范围和以前的内部范围内存之间可能存在大量的内存阻塞/浪费。一旦外部示波器退出,该内存便再次可用。在此之前,堆栈大小会膨胀。这是真的?这可以避免吗?编译器是否阻止了这一点?

+0

“移动语义”并不意味着对象被物理移动到另一个位置。它只意味着由一个对象管理的状态(例如,在堆上分配的一块内存,对象持有一个指针)被以对象的类指定的方式交给另一个对象,这通常比对象的类更便宜制作该州的完整副本。这不会以任何方式影响如何分配或释放这些对象占用的内存,无论是堆栈还是其他内存。 –

+0

这就是我的观点。移动后占用的内存保持不变,但是为了使堆栈正常工作,当范围离开时,范围内的所有内存应该被释放。也许我没有解释清楚。 – bjoern

+0

移动语义仅适用于您有指向堆的指针的类(只要您应该注意)。你可以安全地浅拷贝你的课,因为你知道你对堆数据的所有权是你自己的,你以前的所有权是“移动”给你的。所以当你移动时,你关心的内存永远不会在堆栈中。您仍然复制您的堆栈对象(带有指向堆内存的类项),但与堆中所有数据的深层副本相比,这是小而可接受的。 – Troyseph

回答

0

声明,通俗地说,狡猾的类比和摇摇欲坠的C++知识跟随...

我想我明白你的困惑,

[stack frame a] 
std::vector toPopulate; 
    [stack frame b] 
    std::vector toMove; // will be std::moved into `toPopulate` somehow 
当堆栈范围离开 b并回到 a

,如何在move曾经防止任何复制,你显然不能留在堆栈帧b内存...

回答:不! toMove的堆栈数据被复制到toPopulate,std::vector不将数组的内容存储在堆栈中,它仅存储地址/指针,指向堆中存储器的位置。

什么std::move在这里做的是说给vector不深复制自己(不要堆数据复制到堆中另一个地方),只复制data指针,相信我,这是安全的,安全在编译时执行。

因此toPopulate被构建,它的dataCount和dataPointer以及描述一个std :: vector所需的任何东西都被写入/复制到堆内存中,但是你的堆数据没有被触及,也没有被多个std::vectors在堆栈中,这确实很麻烦!

+0

这是有道理的,并清除了我的另一个关于堆栈大小的混淆......所以我把这些实际数据放到了例如。 STL容器永远不会存储在堆栈上?只有我们称之为向量或地图的元数据在堆栈上?那会很快被复制和释放。并且可以解释为什么我可以在栈上存储数十或数百MB大小的向量。它是否正确? – bjoern

+0

的确,我认为大多数STL容器都是这样工作的。 – Troyseph

相关问题