数组作为模板参数:stack或heap
Array as template parameter: stack or heap?
与堆相比,我对堆栈的了解非常初级,但当涉及到数组时,据我所知,类似的东西是在堆栈上创建的
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
字节。现在一切都有意义了。谢谢
Array<float[n]>* array = new Array<float[n]>(m);
这里发生的是两个堆分配。Array
对象将在堆上分配,因为您使用了new
来创建它。新表达式调用Array
构造函数,后者再次使用new
来分配数组data
;因此CCD_ 14也被分配在堆上。
最好这样做:
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]
;CCD_ 23在最后不进行。
您已经向后写入了数组扩展区。这项工作:
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
类,你会泄露内存和/或双重删除。
在行
Array<float[n]>* array = new Array<float[n]>(m);
在堆上分配了一个Array<T>
的实例。你已经理解了这一点,给出了你关于一般分配的第一个声明。
也许令人困惑的部分是使用float[n]
作为模板参数?
模板参数T
(由Array
定义中的关键字class
表示(表示一种类型。它本身与任何形式的分配都无关。
为了证明这一点,让我们写一个简单的模板,它不使用任何参数:
#include <cassert>
using namespace std;
template <typename T>
class A {
};
int main(){
A<float[100]> a1;
A<float[1000]> a2;
float f[100];
assert(sizeof(a1) == sizeof(a2));
cout << "a1 : " << sizeof(a1) << endl;
cout.<< "f : " << sizeof(f) << endl;
}
输出:
a1 : 1
f : 400
所以这里的CCD_ 33确实是一个类型(1(。
另一方面,当您使用关键字new
时,您知道堆上正在分配一些内容。正如我所说,array
变量将指向堆中的一个内存块。此外,模板本身包含一个堆分配(同样是关键字new
(。
最后,我想详细说明new
表示堆分配的基本前提。默认情况下,当在放置模式中使用时,实际分配很可能在堆栈上。
(1( 请注意,C++接受它,因为n
被声明为常量,因此可以在编译时评估结果类型。删除n
定义中的const
特性,编译器就会抱怨。
- 为什么我的 heap.h 文件给我一个LNK2001错误?
- std::stack 是连续的吗?
- 当为可变性配置时,boost::heap::d_ary_heap 保留的额外 std::list 的目的是什么?
- 为什么我会收到"Run-Time Check Failure #2 - Stack around the variable 'pr' was corrupted"错误?
- 了解 Linux 虚拟内存:valgrind 的 massif 输出显示了有和没有 --pages-as-heap 的主要差异
- Incomings Call with Android Sip stack in Embarcadero C++ bui
- 堆叠协程 + gdb = "previous frame inner to this frame (corrupt stack)?"
- 生成质数的程序,错误:"Stack overflow"
- 如何在 x64 上"stack oveflow"例外
- 为什么此代码中显示"*** stack smashing detected ***: <unknown> terminated Aborted (core dumped) "错误?
- Qt Creator 在执行步骤 "make" 时出现编译错误,-fno-stack-limit
- 无法在基于 DFS 的任务排序程序中填充"stack"
- std::stack的奇怪行为,pop()返回相同的值
- 从 MSVC14 切换到 MSVC16 会导致"compiler is out of heap space (C1060)"错误
- 庞大的初始化列表,如何修复"fatal error C1060: compiler is out of heap space"
- C++-指向对象的指针数组,存储在STACK中的内容和存储在HEAP中的内容
- 为什么在C++内存管理中,术语"automatic"和"dynamic"优先于术语"stack"和"heap"?
- 数组作为模板参数:stack或heap
- C++ Stack and Heap
- 在循环内为类成员声明线程时,需要解释HEAP和STACK