C++ 数组作为模板参数:堆栈还是堆?

C++ 数组作为模板参数:堆栈还是堆?,c++,arrays,templates,heap-memory,stack-memory,C++,Arrays,Templates,Heap Memory,Stack Memory,与堆相比,我对堆栈的了解是非常基本的,但是当涉及到数组时,我知道类似的东西是在堆栈上创建的 float x[100]; 而这样的东西是在堆上创建的 float* x = new float[100]; 但是,如果我创建一个模板数组类,并以“stack”数组类型传递它(比如float[100]),会发生什么呢?例如: #include <iostream> using namespace std; template <class T> class Array { p

与堆相比,我对堆栈的了解是非常基本的,但是当涉及到数组时,我知道类似的东西是在堆栈上创建的

float x[100];
而这样的东西是在堆上创建的

float* x = new float[100];
但是,如果我创建一个模板数组类,并以“stack”数组类型传递它(比如
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;
}
这是怎么回事

编辑:

谢谢大家的语法提示。我真正感兴趣的是街区里发生了什么

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

//在文件范围内
使用add_extent=T[N]的模板;
// ...
添加扩展*数组=新添加扩展[m];
无论是在堆栈上还是在堆上,多维数组都被分配为单个
m*n
元素块。当索引指向数组类型的指针(如
float(*array)[n]
)时,指针将按数组类型的步长一次递增
n
个元素

Array<float[n]>* array = new Array<float[n]>(m);
这将在堆栈上分配
数组
(因此它将在块末尾自动销毁)。但是,尽管
数组
对象本身在堆栈上,但数据仍然存储在堆上,因为它是在
数组
构造函数的堆上分配的。这与使用
std::vector
std::string
局部变量时发生的情况类似

此外,如果没有模板的帮助,我似乎无法做到这一点。具体而言,此代码不编译:

int m = 1000;
const int n = 100;
(float[n])* array = new (float[n])[m];
这只是因为您的语法错误。正确的语法是:

float (*array)[n] = new float[m][n];
左侧显示了声明指向数组的指针的正确方法。对于右侧,需要一个
m
float[n]
s的数组。这表示为
浮动[m][n]
[m]
没有在末尾。

在行中

Array<float[n]>* array = new Array<float[n]>(m);
输出:

a1 : 1
f : 400
因此,
float[n]
这里确实有一个类型(1)

另一方面,当您使用关键字
new
时,您知道堆上正在分配一些内容。正如我所说,
array
变量将指向堆中的内存块。此外,模板本身包含堆分配(同样,关键字
new

最后,我想对
new
表示堆分配这一基本前提进行细微区分。默认情况下,在模式中使用时,实际分配很可能在堆栈上


(1)注意C++之所以接受它,是因为<代码> N< /Cult>被声明为常量,因此在编译时可以对结果类型进行评估。删除
n
的定义的
const
特征,编译器将投诉。

你违反了三人原则。我知道这与这一点无关,但让看到或键入析构函数定义自动敲响警钟并让您考虑复制和赋值是一个好习惯。(如果你不想麻烦的话,可以在C++03中声明它们是私有的,或者在C++11中删除它们);对于(int i=0;i@hunse这是可能的,但与单个块相比效率较低。请注意,
float**
float(*)[N]
@hunse是不同的类型。如果您考虑的是具有多个变量维度的数组,则有些库解决方案仍然为您提供单一分配,并为您执行索引算法,主要是Boost。MultiArray:我刚刚尝试了
Boost::multi_array
,对于基本的创建和数据写入任务,我尝试了它,它比在堆上创建自己的平面数组并自己编制索引要慢得多。无论我使用无优化还是
-O3
(使用g++),这都是正确的。编辑:我应该接着说,使用
-O3
,编写部分只稍微慢一点,但分配对象要慢得多。这比将数组分配为单独的块还要慢,这是令人惊讶的。谢谢。你的陈述“指针按数组类型的步长一次递增n个元素”真的帮助我理解了发生了什么。另外,你的另一个问题在其他答案中也得到了很好的解决。
Array<float[n]>* array = new Array<float[n]>(m);
#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