C++动态创建多维数组
Create a multidimensional array dynamically in C++
在C++中动态创建多维数组有什么好的方法(理解idiomatic/good实践)
例如,假设我有树整数 w
、h
和 d
,我想创建一个数组 MyEnum my_array[w][h][d]
。 (当然w、h、d在编译时是不知道的)。
最好是使用嵌套 std::vector 还是使用新的什么的?
额外的问题:是否也可以动态设置维度?
一般来说,嵌套 std::vector
不是一个好主意。分配内存通常是一个更好的计划,它将整个多维数组作为一个连续的块保存,然后索引到它就好像它是多维的一样。这个内存块 可以 通过 new
分配,但除非你需要精确控制它的分配方式(自定义分配器),否则我建议坚持使用单个 std::vector
.
创建一个class来管理这样一个可以动态设置维数的资源并不难。组织这样一个 class 的一个好方法是跟踪分配的内存、每个维度的大小以及每个维度的 步幅模式 。步幅描述了必须递增多少个元素才能到达给定维度上的下一个元素。
这允许高效的索引(只是指针算法),以及非常高效的重塑:只要元素的数量不变,这只需要改变形状和步幅数组。
示例:
这是一个非常基本的 class,它将存储这样一个 double
的动态多维数组。它以行优先顺序存储数据,这意味着最后一个索引变化最快。所以对于二维数组,第一行是连续存储的,然后是第二行,依此类推。
如果需要,您可以重新调整数组的形状,更改维数。还显示了基本元素访问 operator[]
。 class 没有什么特别之处,但您可以扩展它以提供您想要的任何功能,例如迭代器、数据数学运算、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;
}
您可以使用 g++ -std=c++14 -o test test.cc
编译测试程序,假设定义 class 的文件与 test.cc
.
在同一目录中
在C++中动态创建多维数组有什么好的方法(理解idiomatic/good实践)
例如,假设我有树整数 w
、h
和 d
,我想创建一个数组 MyEnum my_array[w][h][d]
。 (当然w、h、d在编译时是不知道的)。
最好是使用嵌套 std::vector 还是使用新的什么的?
额外的问题:是否也可以动态设置维度?
一般来说,嵌套 std::vector
不是一个好主意。分配内存通常是一个更好的计划,它将整个多维数组作为一个连续的块保存,然后索引到它就好像它是多维的一样。这个内存块 可以 通过 new
分配,但除非你需要精确控制它的分配方式(自定义分配器),否则我建议坚持使用单个 std::vector
.
创建一个class来管理这样一个可以动态设置维数的资源并不难。组织这样一个 class 的一个好方法是跟踪分配的内存、每个维度的大小以及每个维度的 步幅模式 。步幅描述了必须递增多少个元素才能到达给定维度上的下一个元素。
这允许高效的索引(只是指针算法),以及非常高效的重塑:只要元素的数量不变,这只需要改变形状和步幅数组。
示例:
这是一个非常基本的 class,它将存储这样一个 double
的动态多维数组。它以行优先顺序存储数据,这意味着最后一个索引变化最快。所以对于二维数组,第一行是连续存储的,然后是第二行,依此类推。
如果需要,您可以重新调整数组的形状,更改维数。还显示了基本元素访问 operator[]
。 class 没有什么特别之处,但您可以扩展它以提供您想要的任何功能,例如迭代器、数据数学运算、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;
}
您可以使用 g++ -std=c++14 -o test test.cc
编译测试程序,假设定义 class 的文件与 test.cc
.