只有在运行时才知道,有没有一种有效的方法来初始化多维向量的大小?
Posted
技术标签:
【中文标题】只有在运行时才知道,有没有一种有效的方法来初始化多维向量的大小?【英文标题】:Is there an efficient way to initialise the size of a multidimensional vector when only known at runtime? 【发布时间】:2021-03-25 14:57:19 【问题描述】:我有一个类,它有一个 3D 矢量作为它的变量之一。这个向量的大小直到运行时才知道。有没有一种有效的方法来初始化这个向量?
例如,我的班级可能是
class Foo
public:
std::vector<std::vector<std::vector<float>>> x;
std::vector<std::vector<std::vector<float>>> y;
std::vector<std::vector<std::vector<float>>> z;
std::vector<std::vector<std::vector<float>>> bar;
int ni;
int nj;
int nk;
使用构造函数
Foo::Foo(std::vector<std::vector<std::vector<float>>> x_,
std::vector<std::vector<std::vector<float>>> y_,
std::vector<std::vector<std::vector<float>>> z_)
x = x_;
y = y_;
z = z_;
ni = x.size();
nj = x[0].size();
nk = x[0][0].size();
std::vector<std::vector<std::vector<float>>> tmp(ni, std::vector<std::vector<float>>(nj, std::vector<float>(nk)));
bar = tmp;
我可以在不分配虚拟变量tmp
的情况下执行上述最后两行吗?
【问题讨论】:
我的建议,不要使用嵌套向量。编写一个包装一维向量的类并重载operator()
以进行索引。这使得创建归零结构非常容易。您可以在这里看到一个玩具示例:***.com/questions/43358369/…
@NathanOliver 或通过代理重载[]
以获得类似于多维向量的语法。
或者那个。需要更多管道,但它确实使它看起来更自然。
强制免责声明:Boost.MultiArray 存在。
感谢您的有用建议,我可能会尝试使用这些选项。
【参考方案1】:
这是你可以做到的(但不要错过阅读结尾):
#include <vector>
class Foo
public:
std::vector<std::vector<std::vector<float>>> x;
std::vector<std::vector<std::vector<float>>> y;
std::vector<std::vector<std::vector<float>>> z;
int ni;
int nj;
int nk;
using inner_type = std::vector<float>;
using middle_type = std::vector<inner_type>;
using outer_type = std::vector<middle_type>;
outer_type bar;
Foo(outer_type x_,
outer_type y_,
outer_type z_) :
x(x_),y(y_),z(z_),
ni(x.size()),
nj(ni ? x[0].size() : 0),
nk(nj ? x[0].size() : 0),
bar( outer_type(ni,middle_type(nj,inner_type(nk))))
;
在执行构造函数主体之前初始化成员,这就是我使用成员初始化列表的原因。我更改了成员的顺序,因为成员按照它们在类定义中出现的顺序进行初始化。访问x[0]
让我有点紧张,所以我尽量确保空向量不会造成严重破坏。
这可以工作并且可以满足您的要求(我希望如此),但是向量中填充了传递给其构造函数的临时对象的副本,这不是很有效。作为替代方案,您可以按照this answer 中的建议调整成员的大小。
最后同样重要的是,重新考虑您是否真的想要std::vector<std::vector<std::vector<float>>>
。如果您需要所有“行”具有相同数量的“列”,那么嵌套向量会让您为不使用的东西付费。此外,std::vector
最吸引人的特性是它的内存局部性。不过,std::vector<std::vector<float>>
中的 float
s 存储在内存的碎片区域中(因为元素不直接存储在向量中)。
具有适当索引转换的平面std::vector<float>
通常是更好的选择。
float& access_element(size_t i, size_t j, size_t k)
return bar[ i *offset_i + j*offset_j + k];
【讨论】:
甚至可以进入成员初始化列表。 你应该尝试摆脱std::vector<std::vector<float>>(nj, std::vector<float>(nk))
。这可能是一个非常昂贵的临时对象。
@NathanOliver 你已经这样做了 :) (我认为值得展示初始化列表的做法,即使调整大小更好)【参考方案2】:
您可以使用resize()
和几个for 循环来设置bar
。这不是最漂亮的解决方案,但它应该具有相当好的性能,因为没有创建临时对象并且没有不必要的分配。看起来像
bar.resize(ni);
for(auto& twodee : bar)
twodee.resize(nj);
for(auto& onedee : twodee)
onedee.resize(nk);
现在bar
的大小相同,并用零填充。
【讨论】:
以上是关于只有在运行时才知道,有没有一种有效的方法来初始化多维向量的大小?的主要内容,如果未能解决你的问题,请参考以下文章
为啥 SqlDataReader 到 DataTable 只有在我使用单独的字段时才有效?