Массив как параметр шаблона: стек или куча?

Мои знания стека по сравнению с кучей очень просты, но когда дело доходит до массивов, из того, что я знаю, в стеке создается нечто подобное

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 байт. Теперь все это имеет смысл. Спасибо!

6

Решение

Вы написали свои экстенты массива задом наперед. Это работает:

  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 элементы за один раз, в соответствии с типом массива.

2

Другие решения

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

Что здесь происходит два выделение кучи. Array объект будет размещен в куче, потому что вы использовали new создать его. новое выражение вызывает 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] не идет в конце.

3

Вся память уходит в кучу. Компилятор выделяет один гигантский кусок памяти для массива массивов и устанавливает индексирование так, чтобы он был доступен.

И как в стороне, если кто-либо когда-либо копирует или назначает ваш Array класс вы утечки памяти и / или двойного удаления.

1

В соответствии

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

Экземпляр Array<T> выделяется в куче. Вы уже поняли это, учитывая ваше первое утверждение о распределении в целом.

Возможно, запутанной частью является использование float[n] в качестве параметра шаблона?

Параметр шаблона T, как обозначено ключевым словом class в вашем определении Array, представляет тип. Само по себе это не связано с какой-либо формой распределения.

В качестве демонстрации этого давайте напишем простой шаблон, который не использует его параметр:

#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

Так float[n] здесь действительно тип(1).

С другой стороны, когда вы используете ключевое слово newвы знаете, что что-то выделяется в куче. Итак, как я уже сказал, array переменная будет указывать на кусок памяти в куче. Кроме того, сам шаблон содержит выделение кучи (опять же, ключевое слово new).

Наконец, я хотел бы нюансировать основную предпосылку, что new указывает на выделение кучи. Хотя по умолчанию это так, когда используется в размещение режиме, фактическое распределение вполне может быть в стеке.



(1) Обратите внимание, что C ++ принимает его как таковой, потому что n объявляется как константа, и, таким образом, результирующий тип может быть оценен во время компиляции. Удалить const черта определения nи компилятор будет жаловаться.

1
По вопросам рекламы [email protected]