2010-02-05 5 views
126

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

+15

Если вы хотите выбрать хорошую привычку на C++, тогда не используйте массивы напрямую и вместо этого используйте вектор. Vector будет инициализировать все элементы независимо от типа, а затем вам не нужно будет вызывать оператор delete []. – brianegge

+0

@brianegge: Что делать, если мне нужно передать массив внешней функции C, могу ли я просто дать ему вектор? – dreamlax

+11

Вы можете передать '& vector [0]'. – jamesdlin

ответ

302

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

new int[10](); 

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

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

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

...

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

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

+1

Хотя я согласен с тем, что это малоизвестно, я не могу (полностью) согласиться с тем, что это действительно очень удивительно - он был добавлен в C++ 03, который, по-видимому, почти игнорируется большинством людей (поскольку это был один из немногих новых вещи, которые он добавил). –

+1

@ Джерри: Я должен признать, что я еще не знал (вероятно, потому, что, когда я дочитал стандарт, это уже был C++ 03). Тем не менее, замечательно, что все реализации, которые я знаю, поддерживают это (я думаю, это потому, что это так тривиально реализовать). –

+1

Да, это довольно тривиально реализовать. Что касается новой, * all * "инициализация значения" была новой в C++ 03. –

6

Да есть:

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

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

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

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

+10

это не отвечает на вопрос, который был задан. –

+1

Должен ли я всегда использовать 'std :: vector' вместо динамически распределенных массивов? Каковы преимущества использования массива над вектором и наоборот? – dreamlax

+0

Я должен был упомянуть в своем вопросе, что массив должен сохраняться после текущей области. Должен ли я использовать 'new std :: vector'? – dreamlax

23

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

#include <algorithm> 

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

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

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

+0

Я не против, если цикл реализован под функцией, я просто хотел знать, должен ли я сам реализовать такой цикл. Спасибо за совет. – dreamlax

+3

Вы можете быть удивлены. Я был. На моем STL (как GCC, так и Dinkumware) std :: copy фактически превращается в memcpy, если обнаруживает, что он вызывается со встроенными типами. Я не удивлюсь, если std :: fill_n использует memset. –

+2

Nope. Используйте «Value-Initialization», чтобы установить всех членов в 0. –

2

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

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

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

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

0

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

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

7

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

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

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 
1

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

int myArray[10]; 
memset(myArray, 0, 10 * sizeof(int)); 
+0

Я понимаю, что я могу использовать 'memset', но я не был уверен, что это был способ C++ для решения этой проблемы. – dreamlax

+0

Yup, memset работает так же хорошо, как и в C++, как в C для установки линейной памяти на значение. –

+1

На самом деле это не «путь на С ++», но и не массивы. –

17

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

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

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 

Еслидоступны, используя initializer list функции

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 
+0

должен быть вектором Если вы добавили p = new int [10](), у вас был полный список. – karsten

+0

@karsten Исправлено, приветствия. – mloskot

Смежные вопросы