【问题标题】:Is it possible in c++ for a class to have a member which is a multidimensional array whose dimensions and extents are not known until runtime?在 C++ 中,一个类是否有可能拥有一个成员,该成员是一个多维数组,其维度和范围直到运行时才知道?
【发布时间】:2013-12-12 03:19:45
【问题描述】:

我最初问过 using nested std::array to create an multidimensional array without knowing dimensions or extents until runtime 但这有 The XY Problem 试图用 std::array 完成它。

问题 One-line initialiser for Boost.MultiArrayHow do I make a multidimensional array of undetermined size a member of a class in c++? 及其答案提供了一些有用的信息,如何使用 Boost::MultiArray 避免在运行时需要知道维度的范围,但未能演示如何拥有一个类成员可以存储一个数组(在运行时创建),其维度和范围直到运行时才知道。

【问题讨论】:

  • 你的意思是在编译时维度的数量是未知的,还是有N个维度的未知大小?
  • 据我所知(快速查看 boost 文档),您可以按照我的预期简单地指定构造函数中的所有内容
  • 如果您查看我第一次尝试的解决方案:stackoverflow.com/a/20207616/2963099,然后为构造函数保留一个维度向量和可变参数,您应该知道如何做到这一点
  • @ZacHowland 我的意思是维度的数量以及它们的范围(大小)在运行时都是未知的。
  • 格伦的回答将解决您的问题(坦率地说,这是唯一想要解决的问题),但听起来您仍然遭受 XY 问题的困扰。为什么您认为需要动态数量的维度?

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


【解决方案1】:

是的。具有单个指针成员。

一个n多维数组实际上是一个指针。所以你可以分配一个动态的 n 数组并进行强制转换,并将这个数组放在成员指针中。

你的班级应该是这样的

int * holder;

void setHolder(int* anyArray){
  holder = anyArray;
}

使用:

int *** multy = new int[2][1][56];
yourClass.setHolder((int*)multy);

【讨论】:

  • 不,只是不。这就是我要说的。
  • 好的,请解释一下。我们都只学习...我会很高兴学习新的东西...
  • 这与void * multy = (void *)new vector<string>[25]; yourClass.setHolder((int*)multy); 相同,可能不是很安全
  • 类型转换必须是动态的,就维度的数量及其范围而言。我不确定这是否可能,但我真的不认为这是可取的;我的直觉告诉我,这样的解决方案将涉及大量丑陋的 c 风格指针和内存管理。
  • 第一个 never use C 样式转换,第二个尝试避免同时使用 C 样式数组和 naked pointers
【解决方案2】:

避免使用多维数组:

template<typename T>
class Matrix
{
    public:
    Matrix(unsigned m, unsigned n)
    :   n(n), data(m * n)
    {}

    T& operator ()(unsigned i, unsigned j)  {
        return data[ i * n + j ];
    }

    private:
    unsigned n;
    std::vector<T> data;
};

int main()
{
    Matrix<int> m(3, 5);
    m(0, 0) = 0;
    // ...
    return 0;
}

3D 访问(在适当的 3D 矩阵中)将是:

T& operator ()(unsigned i, unsigned j, unsigned k)  { 
    // Please optimize this (See @Alexandre C) 
    return data[ i*m*n + j*n + k ]; 
}

获取任意维度和范围将遵循该方案并添加重载(和维度/范围信息)和/或利用可变参数模板。

您可以在上面避免很多维度(即使在 C++11 中)并用 std::vector 替换参数。例如:T& 运算符(std::vector 索引)。 每个维度(除了最后一个维度)都有一个存储在向量 n 中的扩展(作为上面二维示例中的第一个维度)。

【讨论】:

  • 这个问题没有被标记为 C++11,所以我猜作者没有访问可变参数模板的权限,并且为每个昏暗添加重载是一个非常糟糕的主意......如果数据是 50D(假设可能发生)怎么办?
  • @PawełStawarz 我的错。我将添加 c++11 标签,因为这就是我正在使用的标签,并且如果可能的话,我更愿意使用花哨的 c++11 语法/方法。
  • 宁可(i * m + j) * n + k。这样,如果您添加维度,递归执行起来会更容易。此外,它还保存了一个乘法(编译器可能已经注意到了,顺便说一句)。
【解决方案3】:

您至少可以通过两种方式解决问题,具体取决于您的喜好。首先 - 你不需要 Boost 库,你可以自己做。

class array{
   unsigned int dimNumber;
   vector<unsigned int> dimSizes;

   float *array;

   array(const unsigned int dimNumber, ...){
     va_list arguments;
     va_start(arguments,dimNumber);
     this->dimNumber = dimNumber;
     unsigned int totalSize = 1;
     for(unsigned int i=0;i<dimNumber;i++)
     {
        dimSizes.push_back(va_arg(arguments,double));
        totalSize *= dimSizes[dimSizes.size()-1];
     }
     va_end(arguments);
     array = new float[totalSize]; 
   };

   float getElement(unsigned int dimNumber, ...){
     va_list arguments;
     va_start(arguments,dimNumber);
     unsgned int elementPos = 0, dimAdd = 1;
     for(unsigned int i=0;i<dimNumber;i++)
     {
        unsigned int val = va_arg(arguments,double);
        elementPos += dimAdd * val;
        dimAdd *= dimsizes[i];
     }
     return array[elementPos]
   };
};

设置元素值是相同的,您只需要指定新值。当然你可以使用任何你想要的类型,而不仅仅是浮点数……当然记得delete[]析构函数中的数组。

我还没有测试过代码(只是从内存直接写下来),所以计算位置可能会出现一些问题,但我相信如果你遇到它们,你会修复它们。这段代码应该会给你大致的想法。

第二种方法是创建一个dimension 类,该类将存储一个vector&lt;dimension*&gt;,该vector&lt;dimension*&gt; 将存储子维度。不过这里写的有点复杂,太长了。

【讨论】:

  • -1 用于 DIY 和 NIHS。通常,请在将代码发布为权威答案之前对其进行测试。具体来说,如果您认为自己可以比 Boost 做得更好,那么举证责任就在您身上。
【解决方案4】:

您可以使用具有等量索引的一维数组来代替多维数组。我无法测试此代码,但我希望它能够工作或让您了解如何解决您的问题。您应该记住,从编译时开始就没有固定长度的数组应该通过malloc() 分配,否则您的代码可能无法在其他计算机上运行。 (也许你应该为下面的代码创建一个类array

#include <malloc.h>

int* IndexOffset; //Array which contains how many indices need to be skipped per  dimension
int  DimAmount;   //Amount of dimensions
int  SizeOfArray = 1; //Amount of indices of the array

void AllocateArray(int* output,         //pointer to the array which will be allocated
                   int* dimLengths,     //Amount of indices for each dimension: {1D, 2D, 3D,..., nD}
                   int dimCount){       //Length of the array above

DimAmount = dimCount;
int* IndexOffset = (int*) malloc(sizeof(int) * dimCount);
int temp = 1;

for(int i = 0; i < dimCount; i++){

temp = temp * dimLengths[i];
IndexOffset[i] = temp; 

}


for(int i = 0; i < dimCount; i++){

SizeOfArray = SizeOfArray * dimLengths[i];

}

output = (int*)malloc(sizeof(int) * SizeOfArray);

}

要获取索引,请使用:

int getArrayIndex(int* coordinates //Coordinates of the wished index as an array (like dimLengths)
            ){

int index;
int temp = coordinates[0];

for(int i = 1; i < DimAmount; i++){

     temp = temp + IndexOffset[i-1] * coordinates[i];

}
index = temp;
return index;
}

当您不再需要您的阵列时,请记住free()

for(int i = 0; i < SizeOfArray; i++){

    free(output[i]);

}
free(output);

【讨论】:

    猜你喜欢
    • 2013-03-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-05-12
    • 1970-01-01
    • 2018-09-27
    • 2013-06-05
    • 2013-12-11
    相关资源
    最近更新 更多