数组作为模板参数:堆栈还是堆?
我的堆与堆相比的知识是非常基本的,但是当涉及到数组时,从我知道的东西这样的东西在堆栈上创建
float x[100];
而像这样的东西是在堆上创建的
float* x = new float[100];
但是,如果我创建一个模板数组类,并将其传入“堆栈”数组类型(如float[100]
),会发生什么? 例:
#include <iostream>
using namespace std;
template <class T>
class Array {
public:
int size;
T* data;
Array(int size_) : size(size_) {
data = new T[size];
}
~Array() {
delete [] data;
}
};
int main() {
int m = 1000000;
const int n = 100;
Array<float[n]>* array = new Array<float[n]>(m);
for (int i = 0; i < m; i++)
for (int j = 0; j < n; j++)
array->data[i][j] = i * j;
cout << array->data[10][9] << endl;
delete array;
}
这里到底发生了什么? 这个内存是在栈上还是在堆上创建的? 我的猜测是堆,但这是如何工作的? 编译器是否分配一个大块内存,然后存储指向它的每n
元素的指针? 还是它分配了许多较小的内存块(不一定是连续的),并将指针存储到每个块?
此外,如果没有模板的帮助,我似乎无法做到这一点。 具体来说,这段代码不能编译:
int m = 1000;
const int n = 100;
(float[n])* array = new (float[n])[m];
这里发生了什么?
编辑:
感谢大家的语法提示。 我真正感兴趣的是这个街区发生了什么
int m = 1000;
const int n = 100;
float (*array)[n] = new float[m][n];
但我不知道如何在不使用模板的情况下编写它。 有一件事我真正感兴趣的是,如果编译器将它分配在堆上的一个大块中,那么如何使用语法array[i][j]
访问某个特定元素而不存储指向每个第n个元素的指针? 然后我意识到由于n
是常量,所以sizeof(float[n])
是固定的,所以当你创建数组时,编译器会分配一个m
元素数组,其中每个元素都是一个float[n]
,在我的情况下100 * 4 = 400
字节。 现在这一切都有道理。 谢谢!
你已经向后写了你的数组范围。 这工作:
int m = 1000;
const int n = 100;
float (*array)[n] = new float[m][n];
delete[] array;
如果您希望按照相同的顺序保持数组范围,则可以使用类型别名或适当的模板:
using A = float[n];
A* array = new A[m];
要么
// at file scope
template<typename T, unsigned N> using add_extent = T[N];
// ...
add_extent<float, n>* array = new add_extent<float, n>[m];
无论在堆栈还是堆上,多维数组均被分配为m*n
元素的单个块。 当您索引一个指向数组类型的指针(如float (*array)[n]
)时,指针会按照数组类型的步长每次递增n
元素。
Array<float[n]>* array = new Array<float[n]>(m);
这里发生的是两堆分配。 Array
对象将在堆上分配,因为您使用new
来创建它。 new-expression调用Array
构造函数,它再次使用new
来分配数组data
; 因此data
也被分配到堆上。
这样做更好:
Array<float[n]> array(m);
这会在堆栈中分配array
(所以它会在块的末尾自动销毁)。 但是,当array
对象本身位于堆栈上时,数据仍然存储在堆中,因为它在Array
构造函数的堆中分配。 这与当你有一个std::vector
或std::string
局部变量时发生的情况类似。
此外,如果没有模板的帮助,我似乎无法做到这一点。 具体来说,这段代码不能编译:
这只是因为你的语法错了。 正确的语法是:
float (*array)[n] = new float[m][n];
左侧显示了声明指向数组的指针的正确方法。 对于右侧,你需要一个m
float[n]
s的数组。 这被表示为float[m][n]
; [m]
不会结束。
所有的记忆都在堆上。 编译器为阵列分配一个巨大的内存块,并设置索引以便可访问。
另外,如果有人拷贝或分配你的Array
类,你会泄漏内存和/或双重删除。