【问题标题】:C++11 dynamically allocated variable length multidimensional arrayC++11动态分配变长多维数组
【发布时间】:2015-03-11 17:21:50
【问题描述】:

我一直在尝试创建一个可变长度的多维数组。据我了解,您不能在堆栈上创建可变长度数组,但您可以使用动态分配在 C++ 中创建一维可变长度数组。如果这是一个编译器扩展,请纠正我,但它似乎在带有 --pedantic 集的 clang 和 gcc 上运行良好。

int size = 10;
int *ary = new int[size]();

我试图将这个概念扩展到多维数组。这是我的结果。可能性 1 和 2 的问题在于它们需要 constexpr 并且不适用于可变大小。是否有可能让它们中的任何一个接受一个变量作为它的大小?我知道可能性 3,但它缺少 [][] 访问权限,这正是我正在寻找的。​​p>

constexpr int constSize = 10;

//Possibility 1: Only works in C++11
//Creates CONTIGUOUS 2D array equivalent to array[n*n], but with [][] access

int (*ary1)[constSize] = new int[constSize][constSize]();
delete [] ary1;

//Possibility 2:
//Really horrible as it does NOT create a contiguous 2D array
//Instead creates n seperate arrays that are each themselves contiguous
//Also requires a lot of deletes, quite messy

int **ary2 = new int*[constSize];
for (int i = 0; i < n; ++i)
    ary2[i] = new int[constSize];
for (int i = 0; i < n; ++i)
    delete [] ary2;
delete [] ary2;

//Possibility 3:
//This DOES work with non-constexpr variable
//However it does not offer [][] access, need to access element using ary[i*n+j]

int *ary3 = new int[size*size];
delete [] ary3;

【问题讨论】:

  • int *ary = new int[size](); 不是编译器扩展,它是通过动态分配的一维可变长度数组。我很确定这是进行动态分配的最常见原因。
  • 如果将指针包装在类中并使用重载运算符和代理类,则可以为最后一个版本提供[][] 访问权限。或者你只是重载operator() 以获得ary3(x, y) 语法。并摆脱new[] 并使用std::vector
  • 为什么你认为向量解比较慢?它不应该做任何使其比原始动态分配数组慢的事情;如果有的话,对于某些操作,它应该会稍微快一点,并且更灵活,因为它使用新的放置,而不是new[]。您可以使其与动态分配的数组完全相同(即此处的第三个解决方案)。
  • 我正在复制我在下面所说的内容,但基本上我创建了一个大小为 100,000,000 的数组和向量,然后使用 container[i] = i 进行了 for 循环;向量时间为 0.269s,数组时间为 0.004s。
  • @JamesLens:这当然不应该发生。也许你离开边界检查?但即便如此,IMO 的差异也不应该那么大。您可以将您的确切测试用例作为单独的问题发布。

标签: c++ variables c++11 dynamic multidimensional-array


【解决方案1】:

这将创建一个动态分配的二维可变长度数组,维度为wh

std::vector<std::vector<int>> ary4(w, std::vector<int>(h));

可以通过[][]访问:

ary4[x][y] = 0;

但是,它不是连续分配的。要获得一个连续的数组,这里有一个解决方案:

template<typename E>
class Contiguous2DArray
{
public:
    Contiguous2DArray(std::size_t width, std::size_t height)
    : array(width * height), width(width) {}

    E& operator()(std::size_t x, std::size_t y)
    { return array[x + width * y]; }

private:
    std::vector<E> array;
    std::size_t width;
}

可以这样使用:

Contiguous2DArray<int> ary5(w, h);
ary5(x, y) = 0;

【讨论】:

  • 不满足连续内存要求(尽管在执行速度方面该要求可能被高估了)。
  • 这是一个数组数组,不是二维数组。
  • @JamesLens 你说vector 是不连续的,但事实并非如此。此外,operator[] 将由编译器内联,因此最终与原始数组相比,性能几乎没有差异。另外,请记住关于过早优化的引用。顺便说一句,进行了编辑,检查一下。
  • 你知道你可以超载operator[]
  • @zenith ary5[x][y] 是可能的,如果您为 Contiguous2DArray::operator[] 编写一个帮助器类以返回它自己的重载 operator[]ary5[x, y] 将使用不适合此处的内置逗号运算符。
【解决方案2】:

维数是固定的,因为[] 返回的类型会根据维数而变化。通过重复的[](...) 进行访问。第一个模仿 C 风格的数组查找。 (...) 语法必须完整(它必须将 N 参数传递给 N 维数组)。支持两者都需要适度的效率成本。

使用 C++14 特性来节省冗长。没有一个是必不可少的。

使用n_dim_array0 维度会产生不好的结果。

template<class T, size_t N>
struct array_index {
  size_t const* dimensions;
  size_t offset;
  T* buffer;
  array_index<T,N-1> operator[](size_t i)&&{
    return {dimensions+1, (offset+i)* *dimensions, buffer};
  }
  template<class...Is, std::enable_if_t<sizeof...(Is) == N>>
  T& operator()(size_t i, Is...is)&&{
    return std::move(*this)[i](is...);
  }
};
template<class T>
struct array_index<T,0> {
  size_t const* dimension;
  size_t offset;
  T* buffer;
  T& operator[](size_t i)&&{
    return buffer[i+offset];
  }
  T& operator()(size_t i)&&{
    return std::move(*this)[i];
  }
};

template<class T, size_t N>
struct n_dim_array {
  template<class...Szs, class=std::enable_if_t<sizeof...(Szs)==N>>
  explicit n_dim_array( Szs... sizes ):
    szs{ { static_cast<size_t>(sizes)... } }
  {
    size_t sz = 1;
    for( size_t s : szs )
      sz *= s;
    buffer.resize(sz);
  }
  n_dim_array( n_dim_array const& o ) = default;
  n_dim_array& operator=( n_dim_array const& o ) = default;


  using top_level_index = array_index<T,N-1>;
  top_level_index index(){return {szs.data(),0,buffer.data()};}
  auto operator[]( size_t i ) {
    return index()[i];
  }
  using const_top_level_index = array_index<const T,N-1>;
  const_top_level_index index()const{return {szs.data(),0,buffer.data()};}
  auto operator[]( size_t i ) const {
    return index()[i];
  }
  template<class...Is,class=std::enable_if_t<sizeof...(Is)==N>>
  T& operator()(Is...is){
    return index()(is...);
  }
  template<class...Is,class=std::enable_if_t<sizeof...(Is)==N>>
  T const& operator()(Is...is) const {
    return index()(is...);
  }
private:
  n_dim_array() = delete;
  std::array<size_t,N> szs;
  std::vector<T> buffer;
};

live example

不支持for(:) 循环迭代。编写迭代器并不难:我会在 array_index 中完成。

【讨论】:

    猜你喜欢
    • 2014-03-04
    • 2017-03-18
    • 2021-02-03
    • 2020-04-24
    • 2016-04-01
    • 2015-02-10
    • 2011-05-31
    • 2010-11-27
    • 2020-01-24
    相关资源
    最近更新 更多