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

时间:2015-06-24 21:00:08

标签: c++ memory-management vector

如果我有struct instanceData:

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。所以如果我不处理向量(它确实如此),那么就假设它有效。考虑到这一点,我假设它有问题,因为它不知道矢量将扩展到什么尺寸。但是,我认为向量可能会沿着另一个维度增加,像这样?:

enter image description here

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

是的,I know vectors manage their own memory。除此之外,还有。我试图做一些不同的事情。

2 个答案:

答案 0 :(得分:3)

看起来您希望InstanceData.buffer拥有由其他东西分配/取消分配/访问的实际内存空间。然后实体和顶点指针指向此空间。但是通过尝试使用std :: vector,你混合了两种完全不兼容的方法。

1)您可以使用语言和标准库执行此操作,这意味着没有原始指针,没有“新”,没有“sizeof”。

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

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

2)您可以使用oldschool C管理类内部的内存,这意味着对顶点使用N * sizeof(float)。由于这对于新程序员而言极其容易出错(并且对于兽医来说仍然很粗糙),因此必须将所有这些都设置为InstanceData类,并且不允许InstanceData之外的任何代码来管理它们。使用单元测试。提供公共getter功能。对于通过网络的数据结构,或者在读取/写入具有指定格式的文件(Tiff,pgp,z39.50)时,我已经完成了这样的事情。但只是使用困难的数据结构存储在内存中 - 没办法。

您提出的其他一些问题:

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

你没有。向量分配自己的内存并进行管理。您可以告诉它resize()或reserve()空间或push_back,但它会处理它。看http://en.cppreference.com/w/cpp/container/vector

如何在这样的缓冲区中为矢量[sic]分配内存?

你似乎在考虑一个数组。到目前为止你已经离开了伪代码,所以你真的需要通过一个教程来解决问题。你必须用“新”分配。如果你真的需要,我可以为此发布一些入门代码,我会在这里编辑答案。

另外,你说过一些关于向量增加的东西。向量是一维的。你可以制作一个向量的向量,但是我们不能进入那个。

编辑附录:

使用megabuffer的基本思想是在缓冲区中分配所有必需的空间,然后初始化值,然后通过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];}

标题始终是固定大小,只显示一次,并告诉您有多少实体。在你的情况下,没有标题,因为你正在使用成员变量“usedInstances”和“assignednstances”。所以你有一个标题,但它不是分配缓冲区的一部分。但是你不想分配0个字节,所以只需设置usedInstances = 0; allocatedInstances = 0;缓冲液= nullptr;

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

您没有像您发布的链接那样使用比特币ECS。在那里,他在并行数组中有几个固定大小的不同对象。有一个实体,它的质量,它的位置等。所以实体[4]是一个质量等于“质量[4]”的实体,它的加速度是“加速度[4]”。这使用指针算法来访问数组元素。 (内置数组,NOT std :: Array,NOT std :: vector)

数据布局是“Entity1,Entity2,...,EntityN,mass1,mass2,...,massN,position1,position2,...,positionN,velocity1 ......”你明白了。

如果您阅读了这篇文章,您会发现他说的基本上与其他人所说的标准库相同。您可以使用std容器来存储这些数组中的每一个,或者您可以分配一个megabuffer并使用指针和“内置数组”数学来获取每个项目的缓冲区内的确切内存位置。在经典的faux-pas中,他甚至说“这避免了Array类中可能存在的任何隐藏的开销,我们只有一个分配来跟踪。”但是你不知道它是否比std :: Array更快或更慢,并且你引入了很多错误和额外的开发时间来处理原始指针。

答案 1 :(得分:1)

我想我明白你要做什么了。

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

这已在此处得到解答:Call a constructor on a already allocated memory

第二个问题是

instanceData.vertices[i] = vertices;

instanceData.vertices是一个指向Vector的指针,所以你实际上需要编写

(*(instanceData.vertices))[i]  

第三个问题是*(instanceData.vertices)的内容是浮点数,而不是Vector,所以你不应该在那里进行赋值。