Как инициализировать память новым оператором в С++?
Я только начинаю входить на С++, и я хочу забрать хорошие привычки. Если я только что выделил массив типа int
с оператором new
, как я могу инициализировать их все до 0, не зацикливая их все сам? Должен ли я просто использовать memset
? Есть ли способ "С++"?
Ответы
Ответ 1
Это удивительно малоизвестная особенность C++ (о чем свидетельствует тот факт, что никто еще не дал это в качестве ответа), но на самом деле он имеет специальный синтаксис для инициализации значения массива:
new int[10]();
Обратите внимание, что вы должны использовать пустые скобки - вы не можете, например, использовать (0)
или что-то еще (именно поэтому это полезно только для инициализации значения).
Это явно разрешено ISO C++ 03 5.3.4 [expr.new]/15, в котором говорится:
Новое выражение, которое создает объект типа T
инициализирует этот объект следующим образом:
...
- Если новый инициализатор имеет форму
()
, элемент инициализируется значением (8.5);
и не ограничивает типы, для которых это разрешено, тогда как форма (expression-list)
явно ограничена дополнительными правилами в том же разделе, так что она не допускает типы массивов.
Ответ 2
Предполагая, что вам действительно нужен массив, а не std::vector, "С++ way" был бы таким
#include <algorithm>
int* array = new int[n]; // Assuming "n" is a pre-existing variable
std::fill_n(array, n, 0);
Но имейте в виду, что под капотом это по-прежнему на самом деле всего лишь цикл, который присваивает каждому элементу 0 (нет другого способа сделать это, за исключением специальной архитектуры с поддержкой аппаратного уровня).
Ответ 3
Существует несколько методов для выделения массива внутреннего типа, и все эти методы являются правильными, хотя какой из них выбрать, зависит...
Ручная инициализация всех элементов в цикле
int* p = new int[10];
for (int i = 0; i < 10; i++)
{
p[i] = 0;
}
Использование функции std::memset
из <cstring>
int* p = new int[10];
std::memset(p, 0, 10);
Использование алгоритма std::fill_n
из <algorithm>
int* p = new int[10];
std::fill_n(p, 10, 0);
Использование std::vector
std::vector<int> v(10); // elements zero'ed
Если С++ 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
Ответ 4
Если выделенная память - это класс с конструктором, который делает что-то полезное, оператор new вызовет этот конструктор и опустит ваш объект.
Но если вы выделяете POD или что-то, у которого нет конструктора, который инициализирует состояние объекта, тогда вы не можете выделить память и инициализировать это память с новым оператором в одной операции. Однако у вас есть несколько вариантов:
1) Вместо этого используйте переменную стека. Вы можете выделить и default-initialize за один шаг, например:
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
Ответ 5
Да, есть:
std::vector<int> vec(SIZE, 0);
Используйте вектор вместо динамически распределенного массива. Преимущества заключаются в том, что вам не нужно беспокоиться о явном удалении массива (он удаляется, когда вектор выходит из области видимости), а также что память автоматически удаляется, даже если есть исключение.
Изменить: Чтобы избежать дальнейшего прохода вниз от людей, которые не удосужились прочитать комментарии ниже, я должен сделать более понятным, что этот ответ не говорит, что вектор всегда правильный ответ. Но это, безусловно, более С++, чем "вручную", чтобы удалить массив.
Теперь с С++ 11 существует также std:: array, который моделирует массив с постоянным размером (вектор vs, который может расти). Существует также std:: unique_ptr, который управляет динамически распределенным массивом (который может быть объединен с инициализацией, как ответили на другие ответы на этот вопрос). Любой из них является более С++ способом, чем ручная обработка указателя на массив, IMHO.
Ответ 6
std::fill
- один из способов. Принимает два итератора и значение для заполнения региона. Это, или цикл for, будет (я полагаю) быть более С++-способом.
Для того чтобы установить массив примитивных целочисленных типов в 0, memset
отлично, хотя это может вызвать брови. Рассмотрим также calloc
, хотя это немного неудобно использовать из С++ из-за приведения.
Со своей стороны, я почти всегда использую цикл.
(Я не люблю вторгаться в намерения людей, но верно, что std::vector
, при прочих равных условиях, предпочтительнее использовать new[]
.)
Ответ 7
вы всегда можете использовать memset:
int myArray[10];
memset( myArray, 0, 10 * sizeof( int ));
Ответ 8
Обычно для динамических списков элементов используется std::vector
.
Обычно я использую memset или цикл для динамического распределения необработанных данных в зависимости от того, как переменная я ожидаю, что область кода будет в будущем.