Массив смежных новых объектов - PullRequest
2 голосов
/ 21 апреля 2011

Я сейчас заполняю векторный массив элементов так:

  std::vector<T*> elemArray;

  for (size_t i = 0; i < elemArray.size(); ++i)
  {
    elemArray = new T();
  }

Код, очевидно, был упрощен. Теперь, задав еще один вопрос (не связанный с этой проблемой, но связанный с программой), я понял, что мне нужен массив, содержащий new'd объектов (не может быть в стеке, будет переполнен, слишком много элементов) но смежные. То есть, если бы я получил элемент без индекса массива, я смог бы найти индекс массива, выполнив returnedElement - elemArray[0], чтобы получить индекс элемента в массиве.

Я надеюсь, что объяснил проблему, если нет, пожалуйста, дайте мне знать, какие части, и я постараюсь уточнить.

РЕДАКТИРОВАТЬ: Я не уверен, почему ответ с наибольшим количеством голосов не рассматривается. Я пробовал это много раз. Если я пытаюсь выделить такой вектор с более чем 100 000 (приблизительно) элементов, это всегда дает мне ошибку памяти. Во-вторых, мне нужны указатели, как видно из моего примера. Внезапное изменение указателей, чтобы они не были указателями, потребует большого количества переписывания кода (хотя я и хочу это сделать, но это все еще не решает проблему, заключающуюся в том, что такое распределение векторов с несколькими миллионами элементов не работает.

Ответы [ 7 ]

4 голосов
/ 21 апреля 2011

A std::vector<> хранит свои элементы в выделенном массиве кучи, он не будет хранить элементы в стеке. Таким образом, вы не получите переполнения стека, даже если вы сделаете это простым способом:

std::vector<T> elemArray;
for (size_t i = 0; i < elemCount; ++i) {
   elemArray.push_back(T(i));
}

&elemArray[0] будет указателем на (непрерывный) массив из T объектов.

3 голосов
/ 21 апреля 2011

Если вам нужно, чтобы элементы были смежными, а не указатели, вы можете просто сделать:

std::vector<T> elemArray(numberOfElements);

Сами элементы не будут в стеке, vector управляет динамическим распределением памятии, как в вашем примере, элементы будут инициализированы значением.(Строго говоря, копия инициализируется из временного значения, инициализированного значением, но это должно работать так же для объектов, которые допустимо хранить в векторе.)

Я считаю, что ваш расчет индекса должен быть: &returnedElement - &elemArray[0]и это будет работать с vector.При условии, что returnedElement действительно хранится в elemArray.

1 голос
/ 24 июля 2017

Хотя у меня тоже такие же требования, но по другой причине, в основном из-за того, что кеш горячий ... (для небольшого количества объектов)

    int maxelements = 100000;

    T *obj = new T [100000];        

    vector<T *> vectorofptr;

    for (int i = 0; i < maxelements; i++)
    {
        vectorofptr.push_back(&obj[i]);
    }

или

    int sz = sizeof(T);
    int maxelements = 100000;

    void *base = calloc(maxelements, sz); //need to save base for free()

    vector<T *> vectorofptr;
    int offset = 0;

    for (int i = 0; i < maxelements; i++)
    {
        vectorofptr.push_back((T *) base + offset);
        offset += sz;
    }
1 голос
/ 21 апреля 2011

Учитывая, что ваш старый код вызвал переполнение стека, я думаю, что это, вероятно, выглядело так:

Item items[2000000]; // stack overflow

Если это так, то вы можете использовать следующий синтаксис:

std::vector<Item> items(2000000);

Это распределит (и создаст) элементы непрерывно в куче.

1 голос
/ 21 апреля 2011

Ваш оригинальный цикл должен выглядеть примерно так: (хотя он не создает объекты в непрерывной памяти).

for (size_t i = 0; i < someSize ; ++i)
{
    elemArray.push_back(new T());
}

И тогда вы должны знать две основные вещи здесь:

  • elemArray.size() возвращает количество элементов elemArray, которое в данный момент хранится. Это означает, что если вы используете его в условии цикла for, то ваш цикл for станет бесконечным циклом, потому что вы добавляете в него элементы, и поэтому размер вектора будет увеличиваться.
  • elemArray - это вектор T*, поэтому вы можете хранить только T*, и для его заполнения необходимо использовать функцию push_back.
0 голосов
/ 02 апреля 2014

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

  • объекты, на которые указывают ваши сохраненные указатели, не будут непрерывно выравниваться в памяти, так как они былиРаспределяется (динамически или нет) где-то в вашей программе, в результате вы получите непрерывный массив указателей, указывающих на данные, которые могут быть разбросаны по памяти.в качестве эталонной копии ваш контейнер не станет владельцем выделенных данных и, таким образом, удалит только объекты-указатели при уничтожении, а не объекты, на которые они указывают.Это нормально, когда эти объекты не были динамически размещены, в противном случае вам нужно будет обеспечить механизм освобождения в другом месте на указанных объектах, например, просто проходить по указателям и удалять их по отдельности или использовать общие указатели, которые будут выполнять работу за вас при необходимости;)

  • Последнее, но очень важное, что нужно помнить, это то, что если ваш контейнер будет использоваться в динамической среде с возможными вставками и удалениями в случайных местах, вы должны обязательно использоватьстабильный контейнер, так что ваши итераторы / указатели на сохраненные элементы остаются действительными после таких операций, в противном случае вы получите нежелательные побочные эффекты ... Это в случае с std :: vector, который освобождает и перераспределяет дополнительное пространство при вставке новых элементов, а затемвыполнить копию смещенных элементов (если вы не вставляете их в конец), что делает недействительными указатели / итераторы элементов (хотя некоторые реализации обеспечивают стабильность, например, boost :: stable_vector, но штрафздесь то, что вы теряете смежные свойства контейнера, волшебства не существует при программировании, жизнь несправедлива, верно?; -))

С уважением

0 голосов
/ 21 апреля 2011

Выделите большой кусок памяти и используйте placement new для создания в нем ваших векторных элементов:

elemArray[i] = new (GetNextContinuousAddress()) T(); 

(при условии, что вам действительно нужен указатель на индивидуально новые объекты в вашем массиве, и вы знаете причины, по которым это не рекомендуется ..)

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