C ++: как создать массив объектов в стеке? - PullRequest
4 голосов
/ 26 ноября 2008

Рассмотрим следующий фрагмент кода Java.

int N = 10;
Object obj[] = new Object[N];
for (int i = 0; i < N; i++) {
    int capacity = 1000 * i;
    obj[i] = new ArrayList(capacity);
}

Поскольку в Java все объекты находятся в куче, массив не содержат сами объекты, но ссылки на объекты. Также, сам массив также является объектом, поэтому он живет в куче.

Что является эквивалентом в C ++, но с сохранением массива и объектов стек, чтобы избежать, насколько это возможно, необходимости нового и удаления?

Редактировать: изменил код для использования собственного конструктора.

Ответы [ 6 ]

5 голосов
/ 26 ноября 2008

Просто объявив

Object array_of_objects[10];

в C ++ создает 10 построенных по умолчанию объектов типа Object в стеке.

Если вы хотите использовать конструктор не по умолчанию, это не так просто в C ++. Возможно, есть способ с новым размещением, но я не могу сказать вам, что у меня в голове.

РЕДАКТИРОВАТЬ: ссылка на другой вопрос на StackOverflow Как использовать размещение new для массива, объясняется в ответе на этот вопрос здесь, в StackOverflow.

4 голосов
/ 26 ноября 2008

В C ++ невозможно иметь массив в стеке с размером, определенным во время выполнения. Здесь вы используете std :: vector для этого:

int N = 10;
std::vector<Object> obj(N);
// non-default ctor: std::vector<Object> obj(N, Object(a1, a2));
// now they are all initialized and ready to be used

Если размер известен во время компиляции, вы можете просто использовать простой массив:

int const N = 10;
Object obj[N];
// non-default ctor: Object obj[N] = 
//     { Object(a1, a2), Object(a2, a3), ... (up to N times) };
// now they are all initialized and ready to be used

Если вам разрешено использовать boost, лучше использовать boost :: array, поскольку он предоставляет итераторы, как это делают контейнеры, и вы сможете получить его размер с помощью .size ():

int const N = 10;
boost::array<Object, N> obj;
// non-default ctor: boost::array<Object, N> obj = 
//     { { Object(a1, a2), Object(a2, a3), ... (up to N times) } };
// now they are all initialized and ready to be used
3 голосов
/ 26 ноября 2008

Распределение может выполняться «статически» (размер известен во время компиляции) или «динамически» (размер определяется во время выполнения).

Статическое распределение - простая старая

int myarray[10];

Чтобы выделить в стеке, вам нужна функция выделения alloca, которая, по сути, просто увеличивает указатель стека. (или уменьшается ... что угодно). Удаление выполняется автоматически.

int* myarray = (int*) alloca( n*sizeof(int) );

Таким образом, вы можете инициализировать массив в стеке, как показано Nils .

std::vector может работать со стеком, если предоставлен распределитель стека (второй громоздкий шаблонный аргумент vector)

Я предполагаю, что Boost делает именно это.

3 голосов
/ 26 ноября 2008

Для массива объектов ArrayList:

ArrayList obj[10];

Объекты будут инициализированы по умолчанию, что хорошо для пользовательских типов, но может быть не тем, что вы хотите для встроенных типов.

Рассмотрим также:

std::vector<ArrayList> obj(10, ArrayList());

Это инициализирует объекты путем копирования того, что вы передаете в качестве второго параметра. Так что они все одинаковые, но не обязательно по умолчанию. И, как указывает litb, «10» в векторе можно заменить неконстантным выражением, тогда как «10» в объявлении массива не может.

Это фактически не помещает объекты ArrayList в стек, а помещает все 10 в одно выделение из кучи. Таким образом, очень редко могут возникнуть проблемы с производительностью, если вы действительно не можете позволить себе одно выделение. Однако std :: vector находится в стеке, и он удаляет все объекты кучи, которые он использует, когда он уничтожается. Таким образом, для обеспечения того, чтобы ваши ресурсы были освобождены, вектор ведет себя «как если бы» все было в стеке.

Обратите внимание, что смешивание контейнера Object со значениями ArrayList, как вы делаете в своем примере Java-кода, чревато опасностями в C ++. По сути, вы не можете этого сделать, даже если ArrayList расширяет Object, поскольку массив будет содержать хранилище только для 10 объектов, и ArrayList, вероятно, требует больше байтов для хранения, чем Object. В результате любой ArrayList, который вы пытаетесь скопировать в массив, будет «разрезан»: в массив помещается только начальная часть его представления.

Если вам нужен контейнер типа, говорящего о том, что он содержит объекты, но который на самом деле содержит ArrayLists, тогда вам нужен контейнер указателей. Чтобы получить хорошую обработку ресурсов, это, вероятно, означает, что вам нужен контейнер умных указателей.

2 голосов
/ 26 ноября 2008

Вы даже можете размещать переменные номера объектов в стеке. Вы должны смешать C и C ++, чтобы сделать это.

// allocate storage for N objects on the stack
// you may have to call _alloca and include something to use this.
object * data = (object *) alloca (N * sizeof (object));

// initialize via placement new.
for (int i=0; i<N; i++)
  new (&data[i])();

Код не проверен, но в принципе он работает таким образом.

0 голосов
/ 03 декабря 2013

Если вы используете Qt, вы можете использовать QVarLengthArray

Он принимает размер в качестве второго параметра шаблона, и он статически выделяет массив с таким размером и использует его в качестве основы для массива вместо кучи, как это делает std :: vector или QVector. Если вы добавите больше размера, указанного в шаблоне, вместо него будет использовано выделение кучи.

Пример:

//the following ints will all be stored on the stack,
//and a heap allocation is never performed to store the array
QVarLengthArray<int, 10> objArray;
for (int i = 0; i < 8; i++) {
    int capacity = 1000 * i;
    objArray.push_back(capacity);
}

//since it's a class and not a raw array, we can get the array's size
std::cout << objArray.size(); //result is 8

//a heap allocation will be performed if we add an eleventh item,
//since the template parameter of 10 says to only statically allocate 10 items
objArray.push_back(0); //9 items
objArray.push_back(0); //10 items
objArray.push_back(0); //11 items - heap allocation is performed

Если вы останетесь ниже размера параметра шаблона, вы избежите снижения производительности при выделении кучи - у вас фактически будет динамически распределенный массив на основе стека. Единственный недостаток - это бесполезная трата памяти, если вы не используете столько элементов, сколько указано в параметре шаблона: если вы используете слишком мало, пустые места будут потрачены впустую. если вы используете слишком много, вся выделенная область стека теряется.

Иногда эффективность торговли для памяти того стоит, а иногда - нет. Я бы рекомендовал не использовать вслепую этот класс - используйте его, только если вы знаете через профилирование, что выделение кучи std :: vector является одним из узких мест вашей программы.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...