C++ std::vector如何支持大小未知的自定义对象的连续内存

C++ std::vector如何支持大小未知的自定义对象的连续内存,c++,vector,C++,Vector,我正在努力寻找正确的思维模式,并理解std::vector 我以为我知道的 当您创建一个类型为T的向量,然后为该向量保留N个元素时,编译器基本上会查找并保留一个连续的内存块,即N*sizeof(T)bytes。比如说, // Initialize a vector of int std::vector<int> intvec; // Reserve contigious block of 4 4-byte chunks of memory intvec.reserve(4); /

我正在努力寻找正确的思维模式,并理解
std::vector

我以为我知道的 当您创建一个类型为T的向量,然后为该向量保留N个元素时,编译器基本上会查找并保留一个连续的内存块,即
N*sizeof(T)
bytes。比如说,

// Initialize a vector of int
std::vector<int> intvec;

// Reserve contigious block of 4 4-byte chunks of memory
intvec.reserve(4);  // [ | | | ]

// Filling in the memory chunks has obvious behavior:
intvec.push_back(1);  // [1| | | ]
intvec.push_back(2);  // [1|2| | ]
由于我们不知道Foo的每个实例的大小,因此
foovec.reserve()
如何分配内存?此外,如何实现随机访问时间我们不知道要“跳转”到第k个元素需要多远?

的大小

class Foo {

public:
    Foo() = default;
    Foo(std::vector<int> vec): _vec{vec} {}

private:
    std::vector<int> _vec;
};
class-Foo{
公众:
Foo()=默认值;
Foo(std::vector vec):_vec{vec}{}
私人:
std::vector_vec;
};
已知且为常量,内部std::vector在堆中进行分配,因此执行
foovec.reserve(4)没有问题

否则std::vector如何在堆栈中?;-)

当您创建一个T类型的向量,然后为该向量保留N个元素时,编译器基本上会查找并保留一个连续的内存块

编译器不会做这样的事情。它生成代码,在运行时从向量的分配器请求存储。默认情况下,它将委托给
operator new
,它将从运行时系统获取未初始化的存储

我的心智模型会分解为未知/变化大小的自定义对象

用户定义类型实际具有未知大小的唯一方法是它是否不完整,并且不能将向量声明为不完整类型

在代码中的任何一点上,类型是完整的,其大小也是固定的,您可以像往常一样声明一个存储该类型的向量


您的
Foo
已完成,其大小在编译时是固定的。您可以使用
sizeof(Foo)
sizeof(foovec[0])
等进行检查

向量拥有可变的存储量,但不包含在对象中。它只存储一个指针以及保留和使用的大小(或等效的大小)。例如,以下实例:

class toyvec {
  int *begin_;
  int *end_;
  size_t capacity_;
public:
  // push_back, begin, end, and all other methods
};
始终具有固定大小
sizeof(toyvec)=2*sizeof(int*)+sizeof(size\t)+可能有一些填充
。分配一个巨大的内存块,并将
begin
设置为它的开头,对指针本身的大小没有影响



tl;DR C++没有动态调整对象大小。对象的大小由类定义永久固定。C++确实有对象,它拥有和可以动态存储的对象,但这不是对象本身的一部分。

< P>你的类的大小<代码> Foo在编译时是已知的,:ST::vector < /Cord>类具有恒定的大小,因为它所保存的元素在堆上分配。

std::vector<int> empty{};
std::vector<int> full{};
full.resize(1000000);
assert(sizeof(empty) == sizeof(full));
std::vector empty{};
std::向量全{};
完全调整大小(1000000);
断言(sizeof(空)=sizeof(满));
std::vector
empty
full
的两个实例将始终具有相同的大小,尽管它们包含不同数量的元素


如果您想要一个不能调整大小的数组,并且必须在编译时知道它的大小,请使用
std::array

您的大小概念是有缺陷的。
std::vector
的编译时空间大小已知。它还有一个可以使用的运行时大小(这是在运行时分配的,并且向量包含一个指向它的指针)。你可以想象它是这样布置的

+--------+
|        |
|载体|
|        |
|        |
+--------+
|
|
v
+-------------------------------------------------+
|         |         |         |         |         |
|元素|元素|元素|元素|元素|
|         |         |         |         |         |
+-------------------------------------------------+
因此,当你有一个向量的东西,其中有一个向量,每个
元素
成为向量,然后这些点到他们自己的存储在其他地方,比如

+--------+
|        |
|载体|
|        |
|        |
+----+---+
|
|
v
+----+----+---------+---------+
|对象|对象|对象|
|有|有|有|
|向量|向量|向量|
+----+----+----+----+----+----+
|         |         |    +---------+---------+---------+---------+---------+
|         |         |    |         |         |         |         |         |
||+-->+元素|元素|元素|元素|元素|
|         |              |         |         |         |         |         |
|         |              +-------------------------------------------------+
|         |    +-------------------------------------------------+
|         |    |         |         |         |         |         |
|+-->+元素|元素|元素|元素|元素|
|              |         |         |         |         |         |
|              +-------------------------------------------------+
|    +-------------------------------------------------+
|    |         |         |         |         |         |
+--->+元素|元素|元素|元素|元素|
|         |         |         |         |         |
+---------+---------+---------+---------+---------+
通过这种方式,所有向量彼此相邻,但向量中的元素可以位于内存中的任何其他位置。正是出于这个原因,您不希望对矩阵使用
std:vector
。所有子向量都将内存存储到任何位置,因此行之间没有局部性


请注意,这适用于所有支持分配器的容器,因为它们不直接将元素存储在容器中。对于
std::array
,情况并非如此,因为与原始数组一样,元素也是容器的一部分。如果你有
std::vector<int> empty{};
std::vector<int> full{};
full.resize(1000000);
assert(sizeof(empty) == sizeof(full));