Как инициализировать память новым оператором в C ++? - PullRequest
153 голосов
/ 05 февраля 2010

Я только начинаю изучать C ++ и хочу приобрести хорошие привычки. Если я только что выделил массив типа int с оператором new, как я могу инициализировать их все в 0, не просматривая их все самостоятельно? Должен ли я просто использовать memset? Есть ли & ldquo; C ++ & rdquo; способ сделать это?

Ответы [ 8 ]

348 голосов
/ 05 февраля 2010

Это удивительно малоизвестная особенность C ++ (о чем свидетельствует тот факт, что никто еще не дал это в качестве ответа), но на самом деле он имеет специальный синтаксис для инициализации по умолчанию массива (ну, технически это называется «значение-инициализация» в стандарте):

new int[10]();

Обратите внимание, что вы должны использовать пустые скобки - вы не можете, например, использовать (0) или любое другое выражение (поэтому это полезно только для инициализации по умолчанию).

Это явно разрешено ISO C ++ 03 5.3.4 [expr.new] / 15, в котором говорится:

Новое выражение, которое создает объект типа T, инициализирует этот объект следующим образом:

...

  • Если новый инициализатор имеет форму (), элемент инициализируется значением (8.5);

и не ограничивает типы, для которых это разрешено, тогда как форма (expression-list) явно ограничена дополнительными правилами в том же разделе, так что она не допускает типы массивов.

24 голосов
/ 05 февраля 2010

Предполагая, что вы действительно хотите массив, а не std :: vector, "путь C ++" будет таким

#include <algorithm> 

int* array = new int[n]; // Assuming "n" is a pre-existing variable

std::fill_n(array, n, 0); 

Но имейте в виду, что под капотом это по-прежнему фактически цикл, который присваивает каждому элементу значение 0 (на самом деле другого способа сделать это нет, кроме специальной архитектуры с поддержкой аппаратного уровня).

20 голосов
/ 05 февраля 2010

Существует несколько методов для выделения массива внутреннего типа, и все эти методы верны, хотя какой из них выбрать, зависит ...

Ручная инициализация всех элементов в цикле

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

Если доступно 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
7 голосов
/ 05 февраля 2010

Если выделяемая вами память является классом с конструктором, который делает что-то полезное, оператор new вызовет этот конструктор и оставит ваш объект инициализированным.

Но если вы выделяете POD или что-то, что не имеет конструктора, который инициализирует состояние объекта, то вы не можете выделить память и инициализировать эту память с помощью оператора new за одну операцию. Однако у вас есть несколько вариантов:

1) Вместо этого используйте переменную стека. Вы можете выделить и default-initialize за один шаг, например:

int vals[100] = {0};  // first element is a matter of style

2) использовать memset(). Обратите внимание, что если объект, который вы выделяете, не является POD , memsetting это плохая идея. Один конкретный пример: если вы установили класс с виртуальными функциями, вы уничтожите виртуальную таблицу и оставите свой объект в непригодном для использования состоянии.

3) Многие операционные системы имеют вызовы, которые делают то, что вы хотите - выделить в кучу и инициализировать данные для чего-то. Пример Windows будет VirtualAlloc()

4) Обычно это лучший вариант. Избегайте необходимости самостоятельно управлять памятью. Вы можете использовать контейнеры STL для всего, что вы делаете с необработанной памятью, включая распределение и инициализацию всего одним махом:

std::vector<int> myInts(100, 0);  // creates a vector of 100 ints, all set to zero
6 голосов
/ 05 февраля 2010

Да, есть:

std::vector<int> vec(SIZE, 0);

Использовать вектор вместо динамически размещаемого массива. Преимущества включают отсутствие необходимости явно удалять массив (он удаляется, когда вектор выходит из области видимости), а также то, что память автоматически удаляется, даже если выдается исключение.

Изменить: Чтобы избежать дальнейших негативных отзывов от людей, которые не удосужились прочитать комментарии ниже, я должен пояснить, что в этом ответе не говорится, что вектор всегда правильный ответ. Но это, безусловно, более подход C ++, чем «ручная» проверка удаления массива.

Теперь в C ++ 11 есть также std :: array, который моделирует массив постоянного размера (против вектора, который может расти). Существует также std :: unique_ptr, который управляет динамически размещаемым массивом (который можно комбинировать с инициализацией, как и в других ответах на этот вопрос). Любой из них является более C ++ способом, чем ручная обработка указателя на массив, IMHO.

2 голосов
/ 05 февраля 2010

std::fill в одну сторону. Принимает два итератора и значение для заполнения области. Это или цикл for (я полагаю) будет более подходящим для C ++.

Для установки массива примитивных целочисленных типов, в частности, на 0, memset хорошо, хотя это может вызвать удивление. Также рассмотрим calloc, хотя использовать его из C ++ немного неудобно из-за приведения.

Со своей стороны, я почти всегда использую цикл.

(Я не люблю догадываться о намерениях людей, но это правда, что std::vector при всех равных условиях предпочтительнее использования new[].)

1 голос
/ 05 февраля 2010

вы всегда можете использовать memset:

int myArray[10];
memset( myArray, 0, 10 * sizeof( int ));
0 голосов
/ 05 февраля 2010

Обычно для динамических списков элементов используется std::vector.

Обычно я использую memset или цикл для динамического выделения памяти, в зависимости от того, какой переменной я ожидаю эту область кода в будущем.

...