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

Is there an efficient way to initialise the size of a multidimensional vector when only known at runtime?

我有一个 class,它有一个 3D 矢量作为它的变量之一。这个向量的大小直到运行时才知道。有没有一种有效的方法来初始化这个向量?

比如我的class可能是

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 吗?

这就是你可以做到的(但不要错过阅读结尾):

#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))))
    {
    }
};

成员在构造函数主体执行之前被初始化,这就是我使用成员初始化列表的原因。而且我更改了成员的顺序,因为成员是按照它们在 class 定义中出现的顺序进行初始化的。访问 x[0] 让我有点紧张,所以我试图确保空向量不会造成破坏。

这行得通并且做你想做的(我希望),但是向量中填充了传递给它们的构造函数的临时对象的副本,这不是很有效。作为替代方案,您可以按照 .

中的建议调整成员大小

最后一点,如果你真的想要 std::vector<std::vector<std::vector<float>>>,请重新考虑一下。如果您需要所有“行”具有相同数量的“列”,那么嵌套向量会让您为不使用的东西付费。此外,std::vector 最吸引人的特性是它的内存局部性。但是,std::vector<std::vector<float>> 中的 float 存储在内存的碎片区域中(因为元素没有直接存储在向量中)。

具有适当索引转换的平面 std::vector<float> 通常是更好的选择。

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

您可以使用 resize() 和几个 for 循环来设置 bar。它不是最漂亮的解决方案,但它应该具有相当不错的性能,因为不会创建临时对象并且没有不必要的分配。看起来像

bar.resize(ni);
for(auto& twodee : bar)
{
    twodee.resize(nj);
    for(auto& onedee : twodee)
        onedee.resize(nk);
}

现在 bar 具有相同的大小并且用零填充。