【问题标题】:What's the most idiomatic way to allocate a 3D array in C++?在 C++ 中分配 3D 数组最惯用的方法是什么?
【发布时间】:2019-11-20 20:20:48
【问题描述】:

我有以下代码来分配 3d 数组:


int ***allocate3DArray(int y, int x, int r) {
    int ***array = (int ***) malloc(sizeof(int **) * y);
    for (int i = 0; i < y; i++) {
        array[i] = (int **) malloc(sizeof(int *) * x);
        for (int j = 0; j < x; j++) {
            array[i][j] = (int *) malloc(sizeof(int) * r);
            for (int k = 0; k < r; k++) {
                array[i][j][k] = 0;
            }
        }
    }
    return array;

}


void free3d(int ***arr, int y, int x, int r) {
    for (int i = 0; i < y; i++) {
        for (int j = 0; j < x; j++) {
            free(arr[i][j]);
        }
        free(arr[i]);

    }
    free(arr);

}

这似乎有点笨拙,如果我 malloc 失败,很难进行错误处理。

也就是说,它非常符合人体工程学,因为我可以像这样简单地访问一个元素:int a = arr[x][y][z]

什么是初始化 3d 数组的最佳方法,既能提供相同的人体工程学灵活性,又能减轻上述一些缺点?

我试过了:

    std::array < std::array < std::array < int, radius >, image.cols >, image.rows > houghSpace;

但我得到了一个错误:

 error: non-type template argument is not a constant expression
    std::array < std::array < std::array < int, radius >, image.cols >, image.rows > houghSpace;
                                                ^~~~~~
sobel.cpp:117:49: note: initializer of 'radius' is not a constant expression
sobel.cpp:116:15: note: declared here
    const int radius = image.rows / 2;
              ^
sobel.cpp:117:71: error: expected a type
    std::array < std::array < std::array < int, radius >, image.cols >, image.rows > houghSpace;
                                                                      ^
sobel.cpp:117:86: error: C++ requires a type specifier for all declarations
    std::array < std::array < std::array < int, radius >, image.cols >, image.rows > houghSpace;
                                                                                     ^

【问题讨论】:

  • malloc(sizeof(int) * y * x * r);?
  • 如果你想要性能,方法就像第二个例子here。使用一维向量并使用数学来假装它具有多个维度。有很多方法可以做到这一点,但有些库已经这样做了,所以你应该只获得其中一种。
  • 即使我像您一样采用 3 星级程序员方法,我也会将数组的整个内存分配为单个块,然后将指向该连续块的指针存储到二维数组中。
  • 我会说分配 3D 数组最惯用的方法是不这样做。或者换句话说,使用平面一维数组,然后将索引映射到您喜欢的任何维度

标签: c++ arrays


【解决方案1】:

您需要一个长方体形状的 3 维数组。您目前拥有的是锯齿状数组。每个车道可以有独立的大小,并单独分配。这会产生 3(4) 个问题:

  1. 分配次数为x*y+1
  2. 读取任何元素需要 3 次间接内存加载。
  3. 使用的存储空间比需要的多,因为需要保留指向每个分配区域的指针。
  4. 您的代码还需要手动处理内存

出于同样的原因,使用向量的解决方案也很糟糕。它只解决了一个问题——手动分配和释放。否则会更糟,因为它存储了更多的冗余数据。

您想要的是抽象一个连续的内存范围,以便可以将其解释为 3d 数组。

#include <iostream>
#include <memory>
#include <cstdint>

template <typename T>
struct Array3
{
    Array3(size_t w, size_t h, size_t d) :
        m_data(std::make_unique<T[]>(w*h*d)),
        m_width(w),
        m_height(h),
        m_depth(d)
    {
    }

    T& operator()(size_t x, size_t y, size_t z)
    {
        return m_data[(x * m_height + y) * m_depth + z];
    }

    const T& operator()(size_t x, size_t y, size_t z) const
    {
        return m_data[(x * m_height + y) * m_depth + z];
    }

    size_t width() const
    {
        return m_width;
    }

    size_t height() const
    {
        return m_height;
    }

    size_t depth() const
    {
        return m_depth;
    }

    T* data()
    {
        return m_data.get();
    }

    const T* data() const
    {
        return m_data.get();
    }

private:
    std::unique_ptr<T[]> m_data;
    std::size_t m_width;
    std::size_t m_height;
    std::size_t m_depth;
};


int main()
{
    Array3<int> a(10, 20, 30);
    a(5, 10, 15) = 123;
    std::cout << a(5, 10, 15);
}

使用运算符 () 而不是 [] 进行访问。可以使用 [] 完成,但需要更多样板代码和代理对象。

【讨论】:

    【解决方案2】:

    对 C++ 中的数组的一些强烈建议

    • 不得在 C++ 中使用 malloc 和 free
    • 你甚至不应该再使用 NEW 和 DELETE
    • 您应该永远不要对拥有的内存使用原始指针。请改用std::unique_ptrstd::shared_ptr

    所以,千万不要使用如上所示的函数(allocate3DArray,free3d)

    好的。所以,现在我们想切换到std::array,而您想知道错误消息。它们很清楚,如您的示例所示。 std::array 具有静态大小。它不是动态的。所以你需要给一个编译时间常数来定义大小。

    您正在寻找的是std::vector。它是动态的,可以增长,并且还有一个索引运算符。

    它可以与索引运算符[]一起使用,没有限制。

    请看下面的例子

    #include <iostream>
    #include <iterator>
    #include <vector>
    
    int main()
    {
        // Dimensions for the 3 dimensional vector
        size_t xSize{3};
        size_t ySize{4};
        size_t zSize{5};
        int initialValue{0};
    
        // Define 3 dimensional vector and initialize it.
        std::vector<std::vector<std::vector<int>>> array3d(xSize, std::vector<std::vector<int>>(ySize, std::vector<int>(zSize,initialValue)));
    
        array3d[1][2][3] = 42;
    
        std::cout << "Result: " << array3d[1][2][3] << "\n";
    
        return 0;
    }
    

    【讨论】:

    • std::vector&lt;int&gt;&gt;(ySize, std::vector&lt;int&gt;(zSize,initialValue) 这会将每一行映射到同一个向量。遗憾的是,您需要为每一行手动创建一个向量。
    • @parktomatomi 不,向量被复制,没有参考被存储。我错误地对评论投了赞成票,现在无法撤回。
    • 哦,聪明。感谢您的澄清。
    猜你喜欢
    • 2018-05-29
    • 2015-09-20
    • 2018-02-08
    • 1970-01-01
    • 1970-01-01
    • 2013-05-12
    • 1970-01-01
    • 2017-11-10
    • 1970-01-01
    相关资源
    最近更新 更多