Как изменить размер массива элементов структуры

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

Я объявляю массив как T * arr[SIZE], где T — моя структура. Единственная проблема в том, что я не знаю точного размера массива и мне нужно увеличить его размер, если это необходимо. Поэтому я написал функцию для изменения размера:

if( some cond ){
    T * tmpArr[newSIZE];
    memcpy( tmp, db, newSIZE*sizeof(T));
    delete [] arr;
    arr = tmpArr;
}

Но я получаю сообщение об ошибке, что MyClass::T[....] несовместимо с MyClass::T*[SIZE], что, я думаю, отвечает моему выражению arr = tmpArr.

Можете ли вы сказать мне, что я делаю неправильно? И как лучше объявить T * arr[size] или T * arr = new T[size] и как в этом случае изменить размер (и освободить память от старого) массива?

ОБНОВИТЬ:

Спасибо за ответы, я сделал соответственно в своей программе:

    T * tmp = new T[newSIZE];
    memcpy( tmp, db, newSIZE*sizeof(T) );
    delete [] db;
    db = tmp;

И теперь я получаю странные вещи, после удаления db и назначения db tmp я пытаюсь распечатать все данные, содержащиеся в db (или tmp), эта странная вещь, которую я получаю:

   Smith2 Michigan ave▒ ACME, Ltd. One ACME roa▒
   Smit▒ Michigan ave` ACME, Ltd. One ACME roa "

   One ACME road#@"▒▒▒▒Michigan ave`▒▒▒▒Smit▒"

   ▒▒ ▒8 Ltd.#Michigan avenuemith4▒aF▒

Если я распечатаю те же данные перед удалением и назначением, я получу обычный текст, который хочу. И после того, как в программе (поскольку у меня не было раньше и, возможно, это проблема с моим кодом, я получаю ошибку сегментации). Кстати, я использую struct из std::string и cygwin в своих окнах. Вы хоть представляете, в чем здесь проблема?


person NGix    schedule 25.03.2013    source источник
comment
Ваш tmpArr представляет собой массив указателей. Это выглядит нежелательно. Но опять же, весь код немного бессмысленный.   -  person Kerrek SB    schedule 25.03.2013
comment
Единственная проблема в том, что я не знаю точного размера массива Вам нужно будет отслеживать этот размер самостоятельно.   -  person Drew Dormann    schedule 25.03.2013
comment
@н.м. Они говорят, что не могут использовать std::vector. :(   -  person Xymostech    schedule 25.03.2013
comment
Черт, мне нужны новые очки для чтения.   -  person n. 1.8e9-where's-my-share m.    schedule 25.03.2013
comment
Я контролирую размер массива и изменение размера, на самом деле пытаюсь изменить размер там, где это необходимо. Также решение с вектором будет намного проще и элегантнее, но, к сожалению, я не могу его использовать   -  person NGix    schedule 25.03.2013


Ответы (5)


T* tmpArr[newSIZE]; объявляет массив указателей переменной длины на T. Обратите внимание, что массивы переменной длины не являются частью стандартного C++ (они являются частью C99, но в C++ доступны только как расширение GCC... таким образом, этот код не компилируется разными компиляторами).

Вполне разумным решением было бы использовать std::vector, но раз вы сами написали, что вам не разрешено его использовать, то вот что можно сделать.

  1. изменить T* tmpArr[newSIZE]; на T* arr = new T[size];
  2. now to resize this array:
    1. allocate new array: T* newArr = new T[newSize]
    2. скопировать элементы из старого массива: memcpy(newArr, arr, size * sizeof(T))
    3. освободить старый массив, используя delete[]: delete[] arr
    4. поскольку delete не изменяет сам указатель, у вас есть неверный (висячий) указатель после шага 3, поэтому назначьте указатель на первый элемент нового массива старому: arr = newArr
  3. отслеживать размер массива, с которым вы работаете

Обратите внимание, что T* arr = new T[size] выделяет память, достаточную для хранения size объектов типа T, и создает эти объекты с помощью конструктора по умолчанию T. Затем адрес первого элемента присваивается arr, что указывает на непрерывный блок памяти, где находятся эти элементы.

person LihO    schedule 25.03.2013
comment
по какой-то странной причине memcpy не работает в C++, если T является классом с другими членами объекта (например, std::string). после выделения нового массива вы должны выполнить итерацию по старому и назначить записи одну за другой в новом массиве, чтобы, по крайней мере, вызывался конструктор копирования по умолчанию и делалась плоская копия. memcpy работает, как и ожидалось, в той же самой функции изменения размера, перенесенной в C со структурой, а вместо std::string используется char[n] или char* + malloc(n*sizeof(char)). - person A4L; 26.03.2013

T arr[N];

Объявление этой формы дает вам массив размера N с автоматическим временем хранения. Это исправлено. Вы не можете изменить размер этого массива. Размер N должен быть константой времени компиляции.

T* arr = new T[N];

Это объявление определяет T* с автоматическим сроком хранения. Однако он также создает массив размером N с длительностью динамического хранения. Здесь размер N не обязательно должен быть константой времени компиляции.

Однако это вам не поможет. Вы по-прежнему не можете изменить размер динамического массива. Вам нужно будет сделать delete[] arr, чтобы уничтожить старый массив, а затем сделать new T[NewSize] и каким-то образом скопировать данные.

Это обязательно запутается, и на самом деле это так. В своем коде вы смешиваете два разных типа распределения. Вы пытаетесь выполнить delete[] arr, несмотря на то, что arr не имеет длительности динамического хранения. Вы просто не можете этого сделать. Вы также не можете присвоить один массив другому, как в arr = tmpArr;.

Вместо этого стандартная библиотека C++ предоставляет ряд типов, называемых контейнерами. Это позволяет очень легко иметь динамически изменяемые последовательности элементов. Например, вам было бы гораздо лучше использовать std::vector<T>.

std::vector<T> arr;

std::vector начинается без элементов. Вы можете добавлять элементы, просто выполнив arr.push_back(value). Вы также можете очень легко изменить размер вектора за один раз, выполнив arr.resize(newSize).

person Joseph Mansfield    schedule 25.03.2013
comment
Спасибо за ваш ответ, но можете ли вы сказать мне, как я могу изменить размер или удалить/скопировать динамический массив без каких-либо ошибок? - person NGix; 26.03.2013

Я предполагаю, что вы пытаетесь удалить массив, который вы создали следующим образом:

T array[size];

Но вы не должны вызывать удаление для этого. Этот массив удаляется, когда он выходит за пределы области видимости. Если вы хотите создать массив и затем освободить его, вам нужно использовать оператор new при объявлении массива. Если вы хотите освободить его, используйте delete.

Но в любом случае вы можете использовать std::vector вместо того, чтобы пытаться сделать это самостоятельно. Если вам интересно узнать об этом, на канале 9 от Microsoft был/есть серия видео Стивена Т. Лававея здесь:

http://channel9.msdn.com/Series/C9-Lectures-Stephan-T-Lavavej-Standard-Template-Library-STL-/C9-Lectures-Introduction-to-STL-with-Stephan-T-Lavavej, который является настоящей жемчужиной.

person Darokthar    schedule 25.03.2013

Простое решение — не изобретать квадратное колесо, а использовать уже круглый и отполированный шаблон std::vector<Type> из стандартной библиотеки.

person David Rodríguez - dribeas    schedule 25.03.2013

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

  • добавьте отсутствующий *, так как вы выделяете массив указателей на T,
  • удалите размер для tmpArr и arr

So:

// arr definition
 T **arr;;

// arr resizing code
if( some cond ){
    T **tmpArr;
    memcpy( tmp, db, newSIZE*sizeof(T *));
    delete [] arr;
    arr = tmpArr;
}

В вашем массиве хранятся указатели на T, а не на T, а размер tmpArr неизвестен во время компиляции, поэтому вы не можете указать его в его типе.

Предупреждение: если новый размер меньше текущего, вы потеряете указатели в конце массива. Вы должны добавить тест на это и удалить эти дополнительные T (если они принадлежат вашему контейнеру) или просто создать исключение, поскольку предполагается, что код только увеличивает размер массива.

person didierc    schedule 25.03.2013