简体   繁体   English

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

[英]C++ Declaring a static contiguous multi-dimensional array on the heap the right way

What is the right way to declare a multi-dimensional array that does not change size during runtime on the heap?在堆上声明一个在运行时不会改变大小的多维数组的正确方法是什么? (ideally in C++11, if there is some feature only available in C++14 (not C++17) I would love to hear about it too, but chances are it would not work for me) (理想情况下在 C++11 中,如果某些功能仅在 C++14(而不是 C++17)中可用,我也很想听到它,但它可能对我不起作用)

I have by now looked through dozens of questions and answers about this topic but none seems to really answer it/some answers conflict with others.到目前为止,我已经浏览了许多关于这个主题的问题和答案,但似乎没有一个真正回答它/一些答案与其他答案冲突。

The following solutions I have found and the problems they seem to have that make them non-viable (most of these taken from SO answers and their comments, examples all given with assuming a 3D-array as the target):我发现的以下解决方案以及他们似乎遇到的问题使它们不可行(其中大部分取自 SO 答案及其评论,示例均以 3D 阵列为目标给出):

  • Normal [][][] array declared with new/declaring an array of pointers用 new/声明指针数组声明的普通 [][][] 数组
    Problem: Non-contiguous in memory, every individual array has its independent location in memory问题:在内存中不连续,每个单独的数组在内存中都有其独立的位置

  • multiple std::arrays/boost::arrays nested inside each other多个 std::arrays/boost::arrays 相互嵌套
    Problem: Non-contiguous in memory, every individual array has its independent location in memory问题:在内存中不连续,每个单独的数组在内存中都有其独立的位置

  • Matrix矩阵
    Problem: Just a container for std::array, same problems apply basically问题:只是 std::array 的容器,同样的问题基本上适用

  • multiple std:vectors nested inside each other多个 std:vectors 相互嵌套
    Problem: Dynamic, pretty much all the other problems mentioned before问题:动态,几乎所有之前提到的其他问题

  • Declare as a single block with a pointer to a normal [] array, then go through the index by calculation during runtime with a function like GetIndex(array,x,y,z)使用指向普通 [] 数组的指针声明为单个块,然后在运行时使用 GetIndex(array,x,y,z) 之类的函数通过计算遍历索引
    Problem: This seems to tick all points, but this solution seems less than ideal because of the significant CPU-overhead this seems to introduce when you need to access/change the elements often问题:这似乎可以解决所有问题,但该解决方案似乎不太理想,因为当您需要经常访问/更改元素时,这似乎会引入大量 CPU 开销

Little bit unrelated to that, I have also had some issues with these solutions if they were in classes and I had to access their values from the outside with the .与此无关,如果这些解决方案在课堂上,我也遇到了一些问题,并且我必须使用 . operator, so I would be even more grateful if someone could tell the correct solution with an example of both correct declaration and correct access of the heap-allocated multidimensional-array as a class-member.运算符,所以如果有人能通过正确声明和正确访问堆分配的多维数组作为类成员的示例来告诉正确的解决方案,我将更加感激。

Normal [][][] array declared with new/declaring an array of pointers Problem: Non-contiguous in memory, every individual array has its independent location in memory普通的 [][][] 数组声明为新的/声明一个指针数组问题:在内存中不连续,每个单独的数组在内存中都有其独立的位置

... ...

Yes, the size is always declared with a #define.是的,大小总是用#define 声明的。 – uncanny – 不可思议

Yeah, C++ multidimensional arrays are tricky and quite good at making C++ code unreadable.是的,C++ 多维数组很棘手,而且非常擅长使 C++ 代码不可读。 Actually, you can create static multidimensional array if sizes are known at compile time, so you will get it allocated as a contiguous chunk of memory .实际上,如果在编译时已知大小,您可以创建静态多维数组,因此您会将其分配为连续的内存块

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

    return 0;
}

The question is how to allocate it on the heap... No problem, just wrap it into a struct, and allocate this struct on the heap.问题是怎么在堆上分配……没问题,把它包装成一个struct,把这个struct分配在堆上。

#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;
}

The std::make_unique call allocates Foo on the heap, and guarantees that memory will be deallocated. std::make_unique调用在堆上分配Foo ,并保证内存将被释放。 Also, you are able to access the array inside and outside of Foo with almost zero amount of boilerplate code.此外,您可以使用几乎为零的样板代码访问Foo内部和外部的数组。 Nice!好的!

The right way is to write/use a multidimensional array class.正确的方法是编写/使用多维数组类。 Multidimensional arrays are fundamental objects throughout computer science and it's (IMO) insane that the STL never included first-class support for multidimensional arrays.多维数组是整个计算机科学中的基本对象,STL 从未包含对多维数组的一流支持,这真是太疯狂了(IMO)。 Internally, the class should allocate a 1d array on the heap (for runtime-sized arrays) and do the arithmetic to convert multidimensional indices into 1d indices.在内部,该类应该在堆上分配一个一维数组(对于运行时大小的数组),并进行算术将多维索引转换为一维索引。

Eigen is a good choice if you're doing numerical work;如果你在做数值工作, Eigen是一个不错的选择; not sure how useful it is for multidimensional arrays of a non-numeric type.不确定它对非数字类型的多维数组有多大用处。

If all but the first dimension are compile-time constants (whether or not the first is), just write new T[x][Y][Z] or (more safely) std::make_unique<T[][Y][Z]>(x) .如果除第一个维度之外的所有维度都是编译时常量(无论第一个维度是否),只需编写new T[x][Y][Z]或(更安全) std::make_unique<T[][Y][Z]>(x) The result is contiguous and the compiler has every opportunity to apply tricks like shifts instead of multiplications if appropriate to the dimensions.结果是连续的,如果适合维度,编译器有一切机会应用诸如移位之类的技巧而不是乘法。 What you can't do with such an entity is pass it as pointer and size to a function expecting a one-dimensional array:你不能对这样的实体做的是将它作为指针和大小传递给一个需要一维数组的函数:

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

because pointer arithmetic is defined only within one T[] array (here, the array a3[0][0] that is a T[Z] ).因为指针算术只在一个T[]数组中定义(这里,数组a3[0][0]是一个T[Z] )。

If the first dimension is also constant, you can use a nested std::array (which has no extra memory overhead in practice) or just如果第一个维度也是常量,则可以使用嵌套的std::array (实际上没有额外的内存开销)或仅使用

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

Either has the advantage that it can be passed and returned by value and used as a standard container element.两者都具有可以按传递和返回并用作标准容器元素的优点。 Such an object can of course also be passed by reference, or you can use an “array parameter”:这样的对象当然也可以通过引用传递,或者您可以使用“数组参数”:

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];
}

Note that g 's parameter type is actually T (*)[Y][Z] , which is why the first bound may be omitted.请注意, g的参数类型实际上是T (*)[Y][Z] ,这就是为什么可以省略第一个边界的原因。 If you have如果你有

A3 *a3;

you would call these as你会称这些为

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

but that's just standard pointer usage and has nothing to do with the array type or the heap allocation.但这只是标准的指针用法,与数组类型或堆分配无关。

Memory is nearly always one-dimensional.记忆几乎总是一维的。 What we see as multi-dimensional arrays in languages are an illusion created by the compiler.我们在语言中看到的多维数组是编译器创造的一种错觉。

Let's take an example.让我们举个例子。

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

These both allocate enough storage for 10 integers and leaving type-safety - as might be in C/C++ - aside, are equivalent from the point of view of a processor.它们都为 10 个整数分配了足够的存储空间,并且保留了类型安全性——就像在 C/C++ 中一样——放在一边,从处理器的角度来看是等效的。 Indeed, you can transform - treat either as having however many dimensions as you like - one into the other through type casting pointers.实际上,您可以通过类型转换指针将任意一个维度转换为任意多个维度。

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

This expression holds true, so forth for all the 10 elements.该表达式适用于所有 10 个元素,依此类推。

It does not matter what the storage class of an array is, they are all really just one dimensional.数组的存储类别是什么并不重要,它们实际上都只是一维的。

Also, I believe it will help if you read about how Turing machines work.另外,我相信如果您阅读有关图灵机的工作原理的信息会有所帮助。 The tape is one dimensional even in UTM and that is the most powerful theoretical machine we have.即使在 UTM 中,磁带也是一维的,这是我们拥有的最强大的理论机器。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM