2013-02-26 2 views
9

В C++ существует несколько веских причин использовать массив C над std::vector. Одной из немногих убедительных причин, по крайней мере, с C++ 03, был тот факт, что невозможно использовать вектор для выделения неинициализированного массива объектов. "Заполнить" конструктор std::vector является:Инициализированные значением объекты в C++ 11 и std :: vector constructor

vector(size_type count, const T& value = T())

Это означает, что ...

int* array = new array[1000000]; 

, вероятно, будет гораздо более эффективным, чем:

std::vector<int> v(1000000); 

... так вектор-конструктор должен будет нуль инициализировать массив целых чисел. Таким образом, при работе с вектором POD нет реального эквивалента malloc; лучшее, что вы можете получить, эквивалентно calloc.

C++ 11, похоже, изменил это, с понятием «инициализация ценности». В C++ 11 std::vector имеет новый конструктор, который принимает одно значение size_type, без аргумента по умолчанию. Это «значение-инициализирует» все элементы вектора. Стандарт C++ 11 различает «инициализацию значения» и «нулевую инициализацию».

Насколько я понимаю, «инициализация значения» эквивалентна вызову конструктора по умолчанию на T. Если T - это тип POD, такой как int, тогда конструктор по умолчанию просто создает неинициализированное целое число. Таким образом, в C++ 11 explicit vector::vector(size_type count) действительно эквивалентен malloc, если T является POD.

Однако мое понимание этого основано на проекте C++ 11, а не на окончательном стандарте.

Вопрос: Мое понимание здесь правильно? explicit vector::vector(size_type count) предоставляет неинициализированный массив (аналогично malloc), если T является POD?

+5

Инициализация значения означает инициализацию нуля для встроенных типов. – juanchopanza

+5

Если вы хотите неинициализированное хранилище, используйте «вектор :: резерв», как всегда. – Pubby

+1

@Channel: C++ 03 также имеет инициализацию значения в отличие от инициализации по умолчанию и инициализации нуля. Единственный соответствующий бит, который был изменен, был сам 'std :: vector'. –

ответ

21

Вопрос: Мое понимание здесь верно? explicit vector::vector(size_type count) предоставляет неинициализированный массив (аналогично malloc), если T является POD?

Нет. Здесь существует разница между C++ 03 и C++ 11, но это не так. Разница в том, что в C++ 03, vector<T>(N) будет по умолчанию строить T, а затем сделать N его копиями для заполнения вектора.

Принимая во внимание, что в C++ 11, vector<T>(N) заселяет вектор по умолчанию: TN раз. Для типов POD эффект идентичен. Действительно, я ожидал бы, что для почти всех типов эффект будет идентичным. Однако для чего-то вроде unique_ptr (тип только для перемещения) разница имеет решающее значение. Семантика C++ 03 никогда не будет работать, так как вы не можете сделать копию типа только для перемещения.

Итак:

vector<unique_ptr<int>> v(10); 

создает вектор из 10 нулевых unique_ptrs (которые не являются копиями друг друга).

В редких случаях, когда это делает разницу, и вы должны поведение C++ 03, которые могут быть легко выполнены с помощью:

vector<T> v(10, T()); 
+3

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

+0

@DaveS: Верно, спасибо. Я обновил ответ с инструкциями о том, как восстановить это поведение. –

6

Примечания: значение инициализации происходит в распределителе, так что если вам хотите, чтобы вектор выполнял инициализацию по умолчанию вместо инициализации значения по умолчанию для встроенных элементов, вы можете сделать что-то вроде:

template<typename T> 
struct DefaultInitAllocator { 
    template<typename U> 
    void construct(U* p) 
    { ::new (static_cast<void*>(p)) U; } 

    template<typename U, typename... Args> 
    void construct(U* p, Args&&... args) 
    { ::new (static_cast<void*>(p)) U(std::forward<Args>(args)...); } 

    // ... rest of the allocator interface 
}; 

// ... 
typedef std::vector<int, DefaultInitAllocator<int>> DefaultInitVectorInt; 
+0

Кажется, что просто использование 'reserve()' меньше хлопот. – jiggunjer

+0

reserve() не позволяет вам юридически получить доступ к пространству. – Nevin

+0

, когда вы push_back после резервирования, нет ассигнований, не так ли? – jiggunjer

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