你写了
[...]将自身复制到新位置[...]
这不是向量的工作方式。矢量数据被复制到新位置,而不是矢量本身。
我的回答应该能让您了解向量是如何设计的。
常见的 std::vector 布局*
注:std::allocator
实际上很可能是一个空类并且std::vector
可能不包含此类的实例。对于任意分配器来说,情况可能并非如此。
在大多数实现中,它由三个指针组成,其中
-
begin
指向堆上向量的数据内存的开头(如果不是则始终在堆上)nullptr
)
-
end
指向矢量数据最后一个元素之后的一个内存位置
->size() == end-begin
-
capacity
向量内存最后一个元素之后的内存位置上的点 ->capacity() == capacity-begin
堆栈上的向量
我们声明一个类型的变量std::vector<T,A>
where T
是任何类型并且A
是一个分配器类型T
(i.e. std::allocator<T>
).
std::vector<T, A> vect1;
这在记忆中是什么样子的呢?
正如我们所看到的:堆上没有任何反应,但变量占用了堆栈上所有成员所需的内存。
它就在那里,并且会一直留在那里,直到vect1
超出范围,因为vect1
只是一个像任何其他类型的对象一样的对象double
, int
管他呢。无论它在堆上处理多少内存,它都会位于其堆栈位置并等待被销毁。
的指针vect1
不要指向任何地方,因为向量是空的。
堆上的向量
现在我们需要一个指向向量的指针,并使用一些动态堆分配来创建向量。
std::vector<T, A> * vp = new std::vector<T, A>;
我们再看一下内存。
我们的 vp 变量在堆栈上,我们的向量现在在堆上。同样,向量本身不会在堆上移动,因为它的大小是恒定的。仅指针 (begin
, end
, capacity
如果发生重新分配,则 ) 将移动到内存中的数据位置。让我们看一下。
将元素推入向量
现在我们可以开始将元素推入向量。让我们看看vect1
.
T a;
vect1.push_back(a);
变量vect1
仍然是原来的位置,但堆上的内存被分配来包含 的一个元素T
.
如果我们再添加一个元素会发生什么?
vect1.push_back(a);
- 堆上为数据元素分配的空间是不够的(因为它只有一个内存位置)。
- 将为两个元素分配一个新的内存块
- 第一个元素将被复制/移动到新存储。
- 旧内存将被释放。
我们看到:新的内存位置不同了。
为了获得更多见解,让我们看看销毁最后一个元素时的情况。
vect1.pop_back();
分配的内存不会改变,但最后一个元素的析构函数将被调用,并且结束指针向下移动一个位置。
如你看到的:capacity() == capacity-begin == 2
while size() == end-begin == 1