在C ++中动态创建多维数组 [英] Create a multidimensional array dynamically in C++

查看:78
本文介绍了在C ++中动态创建多维数组的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在C ++中动态创建多维数组的好方法是什么(理解习惯用法/好的做法).

例如,假设我有树整数whd,并且我想创建一个数组MyEnum my_array[w][h][d]. (当然,w,h和d在编译时是未知的.)

最好使用嵌套的std :: vector还是使用new或其他东西?

奖金问题:是否可以动态设置尺寸?

解决方案

通常,嵌套std::vector并不是一个好主意.通常,分配内存的更好方案是将整个多维数组作为一个连续块保存,然后将其索引为 ,就像它是多维的一样.该内存块可以通过new分配,但是除非您需要对其分配方式进行精确控制(自定义分配器),否则我建议坚持使用单个std::vector.

创建一个类来管理可动态设置维数的资源并不难.组织此类的一种好方法是跟踪分配的内存,每个维度的大小以及每个维度的跨步模式.大步幅描述了要沿给定维度到达下一个元素必须增加多少个元素.

这允许有效的索引编制(仅指针算术),以及非常的有效整形:只要元素的数量不变,这仅需要更改形状和步幅数组. /p>


示例:

这是一个非常基本的类,它将存储double s这样的动态多维数组.它以行优先顺序存储数据,这意味着最后一个索引变化最快.因此,对于2D数组,第一行是连续存储的,第二行是连续存储的,依此类推.

如果需要,您可以调整数组的形状,更改维数.还显示了基本元素访问operator[].该类没有别的花哨的东西,但是您可以扩展它以提供所需的任何功能,例如迭代器,对数据的数学运算,I/O运算符等.

/*! \file dynamic_array.h
 * Basic dynamic multi-dimensional array of doubles.
 */

#ifndef DYNAMIC_ARRAY_H
#define DYNAMIC_ARRAY_H

#include <vector>
#include <numeric>
#include <functional>

class
dynamic_array
{
    public:
        dynamic_array(const std::vector<int>& shape)
            : m_nelem(std::accumulate(shape.begin(), shape.end(),
                        1, std::multiplies<int>()))
            , m_ndim(shape.size())
            , m_shape(shape)
        {
            compute_strides();
            m_data.resize(m_nelem, 0.0);
        }

        ~dynamic_array()
        {
        }

        const double& operator[](int i) const
        {
            return m_data.at(i);
        }

        double& operator[](int i)
        {
            return m_data.at(i);
        }

        const double& operator[](const std::vector<int>& indices) const
        {
            auto flat_index = std::inner_product(
                    indices.begin(), indices.end(),
                    m_strides.begin(), 0);
            return m_data.at(flat_index);
        }

        double& operator[](const std::vector<int>& indices)
        {
            auto flat_index = std::inner_product(
                    indices.begin(), indices.end(),
                    m_strides.begin(), 0);
            return m_data.at(flat_index);
        }

        void reshape(const std::vector<int>& new_shape)
        {
            auto new_nelem = std::accumulate(
                    new_shape.begin(), new_shape.end(),
                    1, std::multiplies<int>());
            if (new_nelem != m_nelem) {
                throw std::invalid_argument("dynamic_array::reshape(): "
                        "number of elements must not change.");
            }
            m_nelem = new_nelem;
            m_ndim = new_shape.size();
            m_shape = new_shape;
            compute_strides();
        }

        const std::vector<int>& shape() const
        {
            return m_shape;
        }

        const std::vector<int>& strides() const
        {
            return m_strides;
        }

        int ndim() const
        {
            return m_ndim;
        }

        int nelem() const
        {
            return m_nelem;
        }

    private:
        int m_ndim;
        int m_nelem;
        std::vector<int> m_shape;
        std::vector<int> m_strides;
        std::vector<double> m_data;

        void compute_strides()
        {
            m_strides.resize(m_ndim);
            m_strides.at(m_ndim - 1) = 1;
            std::partial_sum(m_shape.rbegin(),
                    m_shape.rend() - 1,
                    m_strides.rbegin() + 1,
                    std::multiplies<int>());
        }
};

#endif // include guard

这是该功能的基本演示.

/*! \file test.cc
 * Basic test of the dynamic_array class.
 */
#include "dynamic_array.h"
#include <iostream>

int main(int /* argc */, const char * /* argv */[])
{
    dynamic_array arr({2, 3});
    std::cout << "Shape: { ";
    for (auto& each : arr.shape())
        std::cout << each << " ";
    std::cout << "}" << std::endl;

    std::cout << "Strides: { ";
    for (auto& each : arr.strides())
        std::cout << each << " ";
    std::cout << "}" << std::endl;

    // Reshape array, changing number of dimensions, but
    // keeping number of elements constant.
    arr.reshape({6});
    std::cout << "Shape: { ";
    for (auto& each : arr.shape())
        std::cout << each << " ";
    std::cout << "}" << std::endl;

    // Verify that the stride pattern has now also changed.
    std::cout << "Strides: { ";
    for (auto& each : arr.strides())
        std::cout << each << " ";
    std::cout << "}" << std::endl;

    return 0;
}

假设定义类的文件与test.cc在同一目录中,则可以使用g++ -std=c++14 -o test test.cc编译测试程序.

What is the good way (understand idiomatic/good practice) to dynamically create a multidimensional array in C++.

For example let say I have tree integers w, h and d and I want to create an array MyEnum my_array[w][h][d]. (Of course w, h and d are not known at compile time).

Is it best to use nested std::vector or use new or something ?

Bonus question : Is it possible to set the dimension dynamically too ?

解决方案

In general, nesting std::vector is not a great idea. It's usually a better plan to allocate memory which will hold the entirety of your multidimensonal array as a contiguous block, and then index into it as if it were multidimensional. This memory block could be allocated via new, but unless you need some precise control over how it's allocated (custom allocator), I'd recommend sticking with a single std::vector.

It's not difficult to create a class to manage such a resource in which the number of dimensions can be set dynamically. A good way to organize such a class is to keep track of the allocated memory, the sizes of each dimension, and the stride pattern for each dimension. The strides describe how many elements must be incremented over in order to reach the next element along a given dimension.

This allows efficient indexing (just pointer arithmetic), as well as very efficient reshaping: as long as the number of elements doesn't change, this just requires changing the shape and stride arrays.


Example:

Here's a very basic class which will store such a dynamical multidimensional array of doubles. It stores data in row-major order, meaning that the last index varies the fastest. So for a 2D array, the first row is stored contiguously, followed by the second row, and so on.

You can reshape the array, changing the number of dimensions, if you want. A basic element access operator[] is shown, too. There's nothing else fancy about the class, but you can extend it to provide whatever functionality you want, e.g., iterators, mathematical operations on the data, I/O operators, etc.

/*! \file dynamic_array.h
 * Basic dynamic multi-dimensional array of doubles.
 */

#ifndef DYNAMIC_ARRAY_H
#define DYNAMIC_ARRAY_H

#include <vector>
#include <numeric>
#include <functional>

class
dynamic_array
{
    public:
        dynamic_array(const std::vector<int>& shape)
            : m_nelem(std::accumulate(shape.begin(), shape.end(),
                        1, std::multiplies<int>()))
            , m_ndim(shape.size())
            , m_shape(shape)
        {
            compute_strides();
            m_data.resize(m_nelem, 0.0);
        }

        ~dynamic_array()
        {
        }

        const double& operator[](int i) const
        {
            return m_data.at(i);
        }

        double& operator[](int i)
        {
            return m_data.at(i);
        }

        const double& operator[](const std::vector<int>& indices) const
        {
            auto flat_index = std::inner_product(
                    indices.begin(), indices.end(),
                    m_strides.begin(), 0);
            return m_data.at(flat_index);
        }

        double& operator[](const std::vector<int>& indices)
        {
            auto flat_index = std::inner_product(
                    indices.begin(), indices.end(),
                    m_strides.begin(), 0);
            return m_data.at(flat_index);
        }

        void reshape(const std::vector<int>& new_shape)
        {
            auto new_nelem = std::accumulate(
                    new_shape.begin(), new_shape.end(),
                    1, std::multiplies<int>());
            if (new_nelem != m_nelem) {
                throw std::invalid_argument("dynamic_array::reshape(): "
                        "number of elements must not change.");
            }
            m_nelem = new_nelem;
            m_ndim = new_shape.size();
            m_shape = new_shape;
            compute_strides();
        }

        const std::vector<int>& shape() const
        {
            return m_shape;
        }

        const std::vector<int>& strides() const
        {
            return m_strides;
        }

        int ndim() const
        {
            return m_ndim;
        }

        int nelem() const
        {
            return m_nelem;
        }

    private:
        int m_ndim;
        int m_nelem;
        std::vector<int> m_shape;
        std::vector<int> m_strides;
        std::vector<double> m_data;

        void compute_strides()
        {
            m_strides.resize(m_ndim);
            m_strides.at(m_ndim - 1) = 1;
            std::partial_sum(m_shape.rbegin(),
                    m_shape.rend() - 1,
                    m_strides.rbegin() + 1,
                    std::multiplies<int>());
        }
};

#endif // include guard

Here's a basic demo of the functionality.

/*! \file test.cc
 * Basic test of the dynamic_array class.
 */
#include "dynamic_array.h"
#include <iostream>

int main(int /* argc */, const char * /* argv */[])
{
    dynamic_array arr({2, 3});
    std::cout << "Shape: { ";
    for (auto& each : arr.shape())
        std::cout << each << " ";
    std::cout << "}" << std::endl;

    std::cout << "Strides: { ";
    for (auto& each : arr.strides())
        std::cout << each << " ";
    std::cout << "}" << std::endl;

    // Reshape array, changing number of dimensions, but
    // keeping number of elements constant.
    arr.reshape({6});
    std::cout << "Shape: { ";
    for (auto& each : arr.shape())
        std::cout << each << " ";
    std::cout << "}" << std::endl;

    // Verify that the stride pattern has now also changed.
    std::cout << "Strides: { ";
    for (auto& each : arr.strides())
        std::cout << each << " ";
    std::cout << "}" << std::endl;

    return 0;
}

You can compile the test program with g++ -std=c++14 -o test test.cc, assuming the file defining the class is in the same directory as test.cc.

这篇关于在C ++中动态创建多维数组的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆