Как инициализировать память с помощью оператора new в C++?
Я только начинаю в C++ и я хочу, чтобы забрать некоторые хорошие привычки. Если я только что выделил массив типа int
с new
оператор, как я могу инициализировать их все до 0, не прокручивая их все сам? Должен ли я просто использовать memset
? Есть ли способ" C++ " сделать это?
8 ответов
Это удивительно малоизвестная функция C++ (о чем свидетельствует тот факт, что никто еще не дал этого ответа), но на самом деле у нее есть специальный синтаксис для инициализации массива по умолчанию (ну, технически это называется "value-initialize" в стандарте):
new int[10]();
обратите внимание, что вы должны используйте пустые скобки - вы не можете, например, использовать (0)
или любое другое выражение (поэтому это полезно только для инициализации по умолчанию).
это явно разрешено ISO C++03 5.3.4[expr.new] / 15, в котором говорится:
новое-выражение, которое создает объект типа T инициализирует этот объект следующим образом:
...
- если новый инициализатор имеет форму (), элемент инициализируется значением (8.5);
и не ограничивает типы, для которых это разрешено, тогда как (expression-list)
форма явно ограничен далее правила в том же разделе такие, что он не разрешает типы массивов.
предполагая, что вы действительно хотите массив, а не std:: vector, "c++ way" будет следующим
#include <algorithm>
int* array = new int[n]; // Assuming "n" is a pre-existing variable
std::fill_n(array, n, 0);
но имейте в виду, что под капотом это все еще просто цикл, который присваивает каждому элементу 0 (на самом деле нет другого способа сделать это, исключая специальную архитектуру с поддержкой аппаратного уровня).
существует несколько методов для выделения массива внутреннего типа, и все эти методы являются правильными, хотя какой из них выбрать, зависит...
ручная инициализация всех элементов в цикле
int* p = new int[10];
for (int i = 0; i < 10; i++)
{
p[i] = 0;
}
используя std::memset
С <algorithm>
int* p = new int[10];
std::fill_n(p, 10, 0);
используя std::vector
контейнер
std::vector<int> v(10); // elements zero'ed
если C++0x доступно, используя список инициализатора особенности
int a[] = { 1, 2, 3 }; // 3-element static size array
vector<int> v = { 1, 2, 3 }; // 3-element array but vector is resizeable in runtime
если выделяемая Вами память является классом с конструктором, который делает что-то полезное, оператор new вызовет этот конструктор и оставит ваш объект инициализированным.
но если вы выделяете POD или что-то, что не имеет конструктора, который инициализирует состояние объекта, тогда вы не можете выделить память и инициализировать эту память с помощью оператора new в одной операции. Однако, у вас есть несколько вариантов:
1) Используйте переменную стека вместо. Вы можете выделить и по умолчанию-инициализировать в один шаг, вот так:
int vals[100] = {0}; // first element is a matter of style
2) использовать memset()
. Обратите внимание, что если объект, который вы выделяете, не является POD, memsetting это плохая идея. Один конкретный пример - если вы memset класс, который имеет виртуальные функции, вы сдуете vtable и оставите свой объект в непригодном состоянии.
3) многие операционные системы имеют вызовы, которые делают то, что вы хотите-выделяют в куче и инициализируют данные к чему-то. Примером Windows может быть VirtualAlloc()
4) это, как правило, лучший вариант. Избегайте необходимости управлять памятью самостоятельно. Вы можете использовать контейнеры STL, чтобы сделать почти все, что вы сделали бы с необработанной памятью, включая выделение и инициализацию всего одним махом:
std::vector<int> myInts(100, 0); // creates a vector of 100 ints, all set to zero
Да есть такое:
std::vector<int> vec(SIZE, 0);
используйте вектор вместо динамически выделенного массива. Преимущества включают в себя не нужно беспокоиться о явном удалении массива (он удаляется, когда вектор выходит за рамки), а также, что память автоматически удаляется, даже если есть исключение.
Edit: чтобы избежать дальнейших сокращений от людей, которые не утруждают себя чтением комментариев ниже, я должен сделать более ясным, что в этом ответе не говорится, что вектор всегда правильный ответ. Но это, конечно, более C++ - способ, чем "вручную", чтобы удалить массив.
теперь с C++11 есть также std:: array, который моделирует массив постоянного размера (vs vector, который может расти). Существует также std:: unique_ptr, который управляет динамически выделенным массивом (который может быть объединен с инициализацией, как ответили в других ответах на этот вопрос). Любой из них является более C++ способом, чем ручная обработка указателя на массив, ПО МОЕМУ.
std::fill
Это один из способов. Принимает два итератора и значение для заполнения области. Это, или цикл for, будет (я полагаю) более C++ способом.
для установки массива примитивных целочисленных типов в 0 конкретно,memset
это нормально, хотя это может поднять брови. Рассмотрим также calloc
, хотя это немного неудобно использовать С C++ из-за приведения.
со своей стороны, я в значительной степени всегда использую цикл.
(Я не люблю критиковать людей намерения, но это правда, что std::vector
- это, при прочих равных условиях, предпочтительнее использовать new[]
.)
вы всегда можете использовать функцию memset:
int myArray[10];
memset( myArray, 0, 10 * sizeof( int ));
обычно для динамических списков элементов используется std::vector
.
обычно я использую memset или цикл для динамического выделения необработанной памяти, в зависимости от того, насколько переменной я ожидаю, что эта область кода будет в будущем.