将嵌套 C++ 向量作为内置样式的多维数组传递
Posted
技术标签:
【中文标题】将嵌套 C++ 向量作为内置样式的多维数组传递【英文标题】:Pass nested C++ vector as built-in style multi-dimensional array 【发布时间】:2011-04-24 21:37:21 【问题描述】:如果我在 C++ 中有一个向量,我知道我可以安全地将它作为一个数组(指向包含类型的指针)传递:
void some_function(size_t size, int array[])
// impl here...
// ...
std::vector<int> test;
some_function(test.size(), &test[0]);
使用嵌套向量执行此操作是否安全?
void some_function(size_t x, size_t y, size_t z, int* multi_dimensional_array)
// impl here...
// ...
std::vector<std::vector<std::vector<int> > > test;
// initialize with non-jagged dimensions, ensure they're not empty, then...
some_function(test.size(), test[0].size(), test[0][0].size(), &test[0][0][0]);
编辑:
如果不安全,有什么替代方案,如果我可以更改some_function
的签名,如果我不能?
【问题讨论】:
【参考方案1】:简短的回答是“不”。
此处的元素std::vector<std::vector<std::vector<int> > > test;
不会在连续内存区域中被替换。
【讨论】:
【参考方案2】:您只能期望multi_dimensional_array
指向大小为test[0][0].size() * sizeof(int)
的连续内存块。但这可能不是你想要的。
【讨论】:
所以在内存中我只保证有一个 X*Y 的连续数组大小/计数 Z(Z 是最里面的维度),但不能保证这些块是连续的彼此? @Merlyn Morgan-Graham:std::vectorpush_back
函数通常会在内部调用new T[]
。当T
本身是向量类型时,这将导致对每个数组元素至少调用一次构造函数,从而为每个最里面的向量生成一系列对new T[]
的调用。而且,如果您两次调用new T[]
,则不太可能(对于大多数内存管理器:完全不可能)获得两个连续的内存块。获取向量中任何位置的地址并传递它是错误的。它可能看起来有效,但不要指望它。
原因与为什么向量是向量而不是数组密切相关。与数组不同,我们希望向量动态增长。我们希望插入向量是一个恒定的成本,而不是依赖于向量的大小,就像一个数组,直到你达到数组的分配大小。
那么魔法是如何起作用的呢?当没有更多的内部空间可以将下一个元素添加到向量时,分配的新空间大小是旧空间的两倍。旧空间被复制到新空间,并且不再需要旧空间或不再有效,这使得任何指向旧空间的指针都悬空。分配了两倍的空间,因此插入向量的平均成本是恒定的。
【讨论】:
我相信这是标准规定的,因为你说了什么?也就是说,假设您没有将调用中的向量更改为some_function
。此信息适用于最高调用。如果这不是真的,请告诉我,尽管我想我在搜索这个问题的骗子时找到了确认***代码适用于 SO 的答案。这确实是我的示例代码的第二部分,我怀疑它可能无法正常工作,并且您的答案并未完全涵盖这里。
哦,我想我现在可能明白你的意思了。如果最里面的数组之一被调整大小,那么最顶层的内存配置将不再是连续的(如果曾经是)。【参考方案4】:
使用嵌套向量执行此操作是否安全?
是的,如果您只想访问 inner-most 向量,并且您知道它包含的元素数量,并且您不会尝试访问更多。
但是看到您的函数签名,您似乎想要访问所有三个维度,在这种情况下,不,这是无效的。
另一种方法是您可以为每个 inner-most 向量调用函数some_function(size_t size, int array[])
(如果这样可以解决您的问题);为此,您可以使用此技巧(或类似方法):
void some_function(std::vector<int> & v1int)
//the final call to some_function(size_t size, int array[])
//which actually process the inner-most vectors
some_function(v1int.size(), &v1int[0]);
void some_function(std::vector<std::vector<int> > & v2int)
//call some_function(std::vector<int> & v1int) for each element!
std::for_each(v2int.begin(), v2int.end(), some_function);
//call some_function(std::vector<std::vector<int> > & v2int) for each element!
std::for_each(test.begin(), test.end(), some_function);
【讨论】:
如果我想调用函数void some_function(size_t x, size_t y, size_t z, int* multi_dimensional_array)
怎么办?
@Merlyn:我已经说过,如果你想访问函数中的所有三个维度,那是不可能的,因为不能保证所有最内层的向量都使用连续内存;事实上,这不太可能。
对。我也在寻找最简单的替代方案。我想它会是std::vector<int> array(x * y * z)
,但我想看看是否有人有更好的想法,或者至少是该解决方案的好文章。【参考方案5】:
一个非常简单的解决方案是简单地将嵌套向量的内容复制到一个向量中并将其传递给该函数。但这取决于您愿意承担多少开销。
这很可悲:嵌套向量不是好习惯。将所有内容存储在连续内存中并管理访问的矩阵类确实更高效且不那么丑陋,并且可能允许类似 T* matrix::get_raw()
的内容,但内容的顺序仍然是一个实现细节。
【讨论】:
您知道这种矩阵的现有实现吗? @Merlyn 不幸的是,标准中没有,Boost.ublas 可能是矫枉过正。 codeproject.com/KB/architecture/ymatrix.aspx 看起来不错而且相当完整。不过我没用过。 这看起来也不错。你用过吗? boost.org/doc/libs/1_46_1/libs/multi_array/doc/user.html @Merlyn:标准中实际上有一个,称为valarray
。但是,使用起来很痛苦。就个人而言,我只是计算指数[ grid_size * z + row_size * y + x ]
,因为它真的没有那么多工作。经常推荐multi_array
。
@Potatoswatter:哇,我完全忘记了valarray
。可能是有原因的。【参考方案6】:
简单的答案 - 不,不是。你试过编译这个吗?为什么不直接传递整个 3D 矢量作为参考呢?如果您尝试以这种方式访问旧的 C 代码,那么您将无法访问。
【讨论】:
是的,我编译了它,不,我没想到它会起作用(当我尝试它时,它似乎有问题)。我问了这个问题,以防它以某种方式起作用,而我只是做错了,或者是否有简单的替代方案。【参考方案7】:传递向量或对它的引用会更安全:
void some_function(std::vector<std::vector<std::vector<int>>> & vector);
然后您可以在函数中获取大小和项目,从而减少出错的风险。您可以复制向量或传递指针/引用,具体取决于预期的大小和用途。
如果你需要跨模块传递,那么它会变得稍微复杂一些。
【讨论】:
If you need to pass across modules, then it becomes slightly more complicated
。会涉及什么?理想情况下,我可以使用它来调用现有的 c 样式代码。
如果模块使用不同版本的标准库,您可能会遇到问题。我不确定所有细节,我自己从来没有遇到过问题,但我听说过。另一方面,您获得了一个事实,您只传递了一个参数并且出错的机会更少。根据您正在做的事情,这可能适合您,也可能不适合您。【参考方案8】:
尝试使用 &top_level_vector[0]
并将其传递给需要 int*
的 C 风格函数是不安全的。
为了支持对多维数组的正确 C 样式访问,所有数组层次结构的所有字节都必须是连续的。在 c++ std::vector
中,对于向量 包含 的项是这样,但对于向量本身则不然。如果您尝试获取***向量的地址,ala &top_level_vector[0]
,您将获得向量数组,而不是 int
数组。
向量结构不仅仅是包含类型的数组。它被实现为一个包含指针以及大小和容量簿记数据的结构。因此,问题的std::vector<std::vector<std::vector<int> > >
或多或少是一个层次结构树,用指针缝合在一起。只有该树中的最终叶节点是连续的 int
值块。而且这些内存块中的每一个都不一定与任何其他块相邻。
为了与 C 接口,您只能传递单个 vector
的内容。所以你必须创建一个大小为x * y * z
的std::vector<int>
。或者您可以决定重新构建您的 C 代码以一次处理单个一维数据条带。然后你可以保持层次结构,只传入叶向量的内容。
【讨论】:
我添加了我自己的答案,因为似乎没有其他答案将它们放在一起,或者已更新以添加 cmets 的内容。不过,我从所有答案和 cmets 中得到了理解,所以我也支持最有用的答案。以上是关于将嵌套 C++ 向量作为内置样式的多维数组传递的主要内容,如果未能解决你的问题,请参考以下文章