如何为包含向量的数据结构分配内存?

How can I allocate memory for a data structure that contains a vector?

如果我有一个结构实例数据:

struct InstanceData
{
    unsigned usedInstances;
    unsigned allocatedInstances;
    void* buffer;

    Entity* entity;
    std::vector<float> *vertices;
};

并且我为一个实体分配了足够的内存并且std::vector:

newData.buffer = size * (sizeof(Entity) + sizeof(std::vector<float>)); // Pseudo code
newData.entity = (Entity *)(newData.buffer);
newData.vertices = (std::vector<float> *)(newData.entity + size);

然后尝试将任意大小的向量复制到其中:

SetVertices(unsigned i, std::vector<float> vertices)
{
    instanceData.vertices[i] = vertices;
}

我收到访问冲突读取位置错误。

我已将我的代码切碎以使其简洁,但它基于 Bitsquid's ECS。因此,如果我不处理向量(确实如此),就假设它有效。考虑到这一点,我假设它有问题,因为它不知道向量将缩放到什么大小。但是,我认为向量可能会沿着另一个维度增加,像这样?:

我错了吗?无论哪种方式,如何在这样的缓冲区中为向量分配内存

是的,I know vectors manage their own memory。那不是重点。我正在尝试做一些不同的事情。

我想我明白你想做什么。

有很多问题。第一的。您正在制作一个随机数据缓冲区,告诉 C++ 其中一个 Vector 大小的片段是一个 Vector。但是,您绝不会实际调用 Vector 的构造函数,它将内部的指针和构造初始化为可行的值。

这里已经回答了:Call a constructor on a already allocated memory

第二期是行

instanceData.vertices[i] = vertices;

instanceData.vertices是指向Vector的指针,所以实际上需要写成

(*(instanceData.vertices))[i]  

第三个问题是*(instanceData.vertices)里面的内容是float,不是Vector,所以应该不能在那里赋值。

看起来您希望 InstanceData.buffer 拥有实际内存 space,而其他东西是 allocated/deallocated/accessed。然后实体和顶点指针指向这个space。但是通过尝试使用 std::vector,您混淆了两种完全不兼容的方法。

1) 你可以用语言和标准库做到这一点,这意味着没有原始指针,没有 "new",没有 "sizeof"。

struct Point {float x; float y;} // usually this is int, not float
struct InstanceData {
    Entity entity;
    std::vector<Point> vertices;
}

这是我推荐的方式。如果需要输出为特定的二进制格式进行序列化,只需在save方法中处理即可。

2) 您可以使用旧式 C 管理 class 的内部内存,这意味着对顶点使用 N*sizeof(float)。由于这对于新程序员来说非常容易出错(对于老手来说仍然很粗糙),因此您必须将所有这些都设为 class InstanceData 私有,并且不允许 InstanceData 之外的任何代码来管理它们。使用单元测试。提供 public getter 功能。对于通过网络传输的数据结构,或者当 reading/writing 具有指定格式(Tiff、pgp、z39.50)的文件时,我已经做过这样的事情。但只是使用困难的数据结构存储在内存中——没办法。

您问的其他一些问题:

如何为 std::vector 分配内存?

你不知道。 vector 分配自己的内存,并对其进行管理。您可以告诉它 resize() 或 reserve() space,或 push_back,但它会处理它。看看http://en.cppreference.com/w/cpp/container/vector

如何在这样的缓冲区中为向量 [原文如此] 分配内存?

你好像在想数组。到目前为止,您的伪代码还差得远,因此您确实需要通过教程逐步学习。您必须使用 "new" 进行分配。我可以 post 一些入门代码,如果你真的需要,我会在这里编辑答案。

另外,你说了向量沿另一个维度递增。矢量是一维的。您可以创建向量的向量,但我们先不谈这个。

编辑附录:

巨型缓冲区的基本思想是在缓冲区中分配所有必需的 space,然后初始化值,然后在 getter 期间使用它。

数据布局为"Header, Entity1, Entity2, ..., EntityN"

// I did not check this code in a compiler, sorry, need to get to work soon
MegaBuffer::MegaBuffer() {AllocateBuffer(0);}
MegaBuffer::~MegaBuffer() {ReleaseBuffer();}

MegaBuffer::AllocateBuffer(size_t size /*, whatever is needed for the header*/){
    if (nullptr!=buffer)
        ReleaseBuffer(); 

    size_t total_bytes = sizeof(Header) + count * sizeof(Entity)
    buffer = new unsigned char [total_bytes];
    header = buffer;

    // need to set up the header
    header->count = 0;
    header->allocated = size;

    // set up internal pointer
    entity = buffer + sizeof(Header);
}

MegaBuffer::ReleaseBuffer(){
    delete [] buffer;
}

Entity* MegaBuffer::operator[](int n) {return entity[n];}

header总是一个固定的大小,并且恰好出现一次,并告诉你你有多少个实体。在您的情况下,没有 header 因为您使用的是成员变量 "usedInstances" 和 "allocatednstances" 。所以你确实有一个 header 但它不是分配缓冲区的一部分。但是你不想分配0字节,所以只要设置usedInstances=0;分配实例=0;缓冲区=空指针;

我没有编写更改缓冲区大小的代码,因为 bitsquid ECS 示例涵盖了这一点,但他没有显示第一次初始化。确保初始化 n 和 allocated,并在使用前为每个实体分配有意义的值。

您使用的 bitsquid ECS 与您 post 编辑的 link 不同。在那里,他在并行数组中有几个不同的 objects 固定大小。有一个实体,它的质量,它的位置等。所以实体[4]是一个质量等于"mass[4]"且加速度为"acceleration[4]"的实体。这使用指针算法来访问数组元素。 (内置数组,不是 std::Array,不是 std::vector)

数据布局是 "Entity1, Entity2, ..., EntityN, mass1, mass2, ..., massN, position1, position2, ..., positionN, velocity1 ... " 你懂的。

如果您阅读了这篇文章,您会发现他所说的与其他人对标准库所说的基本相同。您可以使用标准容器来存储这些数组中的每一个,或者您可以分配一个兆缓冲区并使用指针和 "built in array" 数学来为每个项目到达该缓冲区中的确切内存位置。在 classic faux-pas 中,他甚至说 "This avoids any hidden overheads that might exist in the Array class and we only have a single allocation to keep track of." 但是你不知道这比 std::Array 快还是慢,而且你引入了很多错误以及处理原始指针的额外开发时间。