Как инициализировать память новым оператором в С++?

Я только начинаю входить на С++, и я хочу забрать хорошие привычки. Если я только что выделил массив типа 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 или цикл для динамического распределения необработанных данных в зависимости от того, как переменная я ожидаю, что область кода будет в будущем.