向量std :: move之后,向量元素如何保留其原始地址?

时间:2018-12-23 15:25:50

标签: c++ c++11 vector move-semantics

从输出中可以看到,向量pre的对象不仅“移动”到向量post,而且还在存储器中保留了它们的原始地址空间。此举背后的真正含义是什么?这是预期的行为吗?假设我需要有一个指向这些对象的单独的指针向量,可以安全地假设在此移动之后这些对象将始终具有其原始地址吗?

实际上,我有一个类,其中包含一个像这样的向量以及作为成员提到的指针的向量。我还删除了副本控制器,并为该类定义了移动控制器。

#include <iostream>
#include <vector>

struct B {
    int val = 0;   
    B(int aInt) : val(aInt) {  };
};

int main() {

    std::vector<B> pre;

    pre.push_back(B(1));
    pre.push_back(B(2));
    std::cout << "pre-move:\t" << (void*)&pre.at(0) << '\n';
    std::cout << "pre-move:\t" << (void*)&pre.at(1) << '\n';

    std::vector<B> post(std::move(pre));

    std::cout << "post-move:\t" << (void*)&post.at(0) << '\n';
    std::cout << "post-move:\t" << (void*)&post.at(1) << '\n';

    return 0;
}

输出:

pre-move:   0x1d7b150 
pre-move:   0x1d7b154 <------|
post-move:  0x1d7b150        |
post-move:  0x1d7b154 <------|

3 个答案:

答案 0 :(得分:11)

向量基本上只不过是指向堆分配的内存,向量的当前长度和当前容量的指针。

通过“移动”矢量,您要做的就是复制这些值,然后重置从矢量移出的矢量的值。

对于向量的数据,基本上等于

original_pointer = some_place_in_memory;
new_pointer = original_pointer;   // Copies the *value* of original_pointer
original_pointer = nullptr;

无需分配新的内存并将数据复制到向量中。

答案 1 :(得分:1)

移动操作的全部目的是避免复制元素,因此,如果复制了元素(没有真正“移动”内存的东西),则移动将只是复制。

向量通常实现为3个指针:开始,结束和容量。所有都指向动态分配的数组。然后移动向量只是复制这三个指针,因此数组和元素仅更改其所有者。

我认为假定元素的指针保持有效应该是安全的。

答案 2 :(得分:0)

很明显,如果我们编写不带std::vector的语义相等的代码:

B* pre = new B[2]; // Declare std::vector<B> and allocate some space to make the following line correct

B[0] = 1; // pre.push_back(B(1));
B[1] = 2; // pre.push_back(B(2));

B* post = pre; // std::vector<B> post(std::move(pre));

实际上,向量移动归结为指针复制而没有重新分配。指针指向的数据保留在原处,因此矢量元素的地址不会更改。

在此代码示例的第四行之后,prepost都指向具有相同地址的相同数据。

std::vector是带有一些附加功能的数组指针的包装。因此,在完成std::vector<B> post(std::move(pre));之后,post将包含一个指针,该指针的值与pre中的值相同。