C ++以正确的方式在堆上声明静态连续多维数组

声明在堆上运行时不会更改大小的多维数组的正确方法是什么? (理想情况下,在C ++ 11中,如果只有C ++ 14(而非C ++ 17)具有某些功能,我也很想听听它,但可能对我不起作用)

到目前为止,我已经浏览了有关该主题的数十个问题和答案,但似乎没有一个真正的答案/一些答案与其他人冲突。

我发现以下解决方案以及它们似乎不可行的问题(大多数解决方案来自SO答案及其评论,所有示例均以3D阵列为目标给出):>

  • 用新的/声明的指针数组声明的普通[] [] []数组
    问题:在内存中不连续,每个单独的数组在内存中都有其独立的位置

  • 多个std :: arrays / boost :: array彼此嵌套
    问题:在内存中不连续,每个单独的数组在内存中都有其独立的位置

  • 矩阵
    问题:只是std :: array的容器,基本上存在相同的问题

  • 多个std:vector相互嵌套
    问题:动态的,几乎是前面提到的所有其他问题

  • 声明为带有指向常规[]数组的指针的单个块,然后在运行时使用诸如GetIndex(array,x,y,z)之类的函数通过计算来遍历索引
    问题:这似乎可以解决所有问题,但是这种解决方案似乎不太理想,因为在需要经常访问/更改元素时会引入大量的CPU开销

与此无关的一点,如果这些解决方案在类中,那么我也遇到了一些问题,并且我不得不从外部访问它们的值。运算符,因此,如果有人能以一个正确的声明和对作为类成员的堆分配多维数组的正确访问的例子来说明正确的解决方案,我将不胜感激。

lqy33333333 回答:C ++以正确的方式在堆上声明静态连续多维数组

  

用新的/声明的指针数组声明的普通[] [] []数组   问题:内存中不连续,每个单独的数组在内存中都有其独立的位置

...

  

是的,始终使用#define声明大小。 –不可思议

是的,C ++多维数组很棘手,并且非常擅长使C ++代码不可读。实际上,如果在编译时就知道大小,则可以创建静态多维数组,因此可以将其分配为连续的内存块

int main()
{
    int arr[100][200][100]; // allocate on the stack

    return 0;
}

问题是如何在堆上分配它...没问题,只需将其包装到一个结构中,然后在堆上分配该结构即可。

#include <memory>

struct Foo
{
    int arr[100][200][100];
};

int main()
{
    auto foo = std::make_unique<Foo>(); // allocate on the heap
    auto& arr = foo->arr;

    arr[1][2][3] = 42;

    return 0;
}

std::make_unique调用在堆上分配Foo,并保证将释放内存。另外,您可以用几乎为零的样板代码访问Foo内部和外部的数组。很好!

,

正确的方法是编写/使用多维数组类。多维数组是整个计算机科学的基本对象,并且(IMO)疯狂的STL从未包括对多维数组的一流支持。在内部,该类应在堆上分配一个1d数组(用于运行时大小的数组),并进行算术运算以将多维索引转换为1d索引。

Eigen是进行数值工作的理想选择;不确定对非数字类型的多维数组有多大用处。

,

如果除第一个维之外的所有维都是编译时常量(无论第一个维是否为编译时常量),只需编写new T[x][Y][Z]或(更安全地)编写std::make_unique<T[][Y][Z]>(x)。结果是连续的,如果合适的话,编译器将有机会应用诸如移位之类的技巧而不是乘法。对于这样的实体,您不能做的就是将其作为指针和大小传递给需要一维数组的函数:

f(&a3[0][0][0],x*Y*Z);  // undefined behavior

因为指针算术仅在一个T[]数组(此处为a3[0][0]的数组T[Z])中定义。

如果第一维也恒定,则可以使用嵌套的std::array(实际上没有额外的内存开销)

struct A3 {
  T a[X][Y][Z];
};

每种方法都有一个优点,它可以通过 value 传递和返回并用作标准容器元素。这样的对象当然也可以通过引用传递,也可以使用“数组参数”:

T f(A3 &a3) {
  return a3.a[0][0][1]+a3.a[0][1][0]+a3.a[1][0][0];
}
T g(T a[][Y][Z]) {
  return a[0][0][1]+a[0][1][0]+a[1][0][0];
}

请注意,g的参数类型实际上是T (*)[Y][Z],因此可以省略第一个界限。如果有

A3 *a3;

您将其称为

f(*a3);
g(a3->a);

但这只是标准的指针用法,与数组类型或堆分配无关。

,

内存几乎总是一维的。我们认为语言中的多维数组是编译器产生的一种错觉。

让我们举个例子。

int numbers_1[10];
int numbers_2[2][5];

从处理器的角度来看,这两者都为10个整数分配了足够的存储空间,并且保留类型安全性(在C / C ++中可能如此)。确实,您可以通过类型转换指针将其转换为任意一个维度,将其视为任意一个维度。

numbers_1[0] == ((int**)numbers_1)[0][0];

这个表达式成立,对所有10个元素来说都是如此。

数组的存储类无关紧要,它们实际上只是一维。

此外,如果您了解图灵机的工作原理,我相信这会有所帮助。即使在UTM中,磁带也是一维的,这是我们拥有的功能最强大的理论机器。

本文链接:https://www.f2er.com/3016912.html

大家都在问