只有在运行时才知道,有没有一种有效的方法来初始化多维向量的大小?

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&lt;std::vector&lt;std::vector&lt;float&gt;&gt;&gt;。如果您需要所有“行”具有相同数量的“列”,那么嵌套向量会让您为不使用的东西付费。此外,std::vector 最吸引人的特性是它的内存局部性。不过,std::vector&lt;std::vector&lt;float&gt;&gt; 中的 floats 存储在内存的碎片区域中(因为元素不直接存储在向量中)。

具有适当索引转换的平面std::vector&lt;float&gt; 通常是更好的选择。

 float& access_element(size_t i, size_t j, size_t k) 
       return bar[ i *offset_i + j*offset_j + k];
 

【讨论】:

甚至可以进入成员初始化列表。 你应该尝试摆脱std::vector&lt;std::vector&lt;float&gt;&gt;(nj, std::vector&lt;float&gt;(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 只有在我使用单独的字段时才有效?

在 Dart 的构造函数中初始化最终变量。两种方法,但只有一种有效? [复制]

终于知道C#的动态类型有啥用了

检测网络电缆何时拔出

tensorflow为啥变量一定要初始化

Servlet的生命周期