如何为包含向量的数据结构分配内存?
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 快还是慢,而且你引入了很多错误以及处理原始指针的额外开发时间。
如果我有一个结构实例数据:
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 快还是慢,而且你引入了很多错误以及处理原始指针的额外开发时间。