Вибір між вектором :: розмір () та вектор :: резерв ()


151

Я попередньо виділяю деяку пам’ять своїй vectorзмінній члена. Нижче код мінімальна частина

class A {
  vector<string> t_Names;
public:
  A () : t_Names(1000) {}
};

Тепер у якийсь момент часу, якщо t_Names.size()дорівнює 1000. Я маю намір збільшити розмір на 100. Потім, якщо вона досягне 1100, знову збільшуйте 100і так далі.

Моє питання - що вибрати між vector::resize()і vector::reserve(). Чи є кращий вибір у такому сценарії?

Редагувати : У мене є якась точна оцінка для t_Names. Я оцінюю це бути поруч 700з 800. Однак у певних (рідко) ситуаціях він може зростати більше 1000.


34
Ви розумієте, що це означає, що зростання вектора більше не амортизується постійним часом, і ви втрачаєте одну з переваг продуктивності використання std::vector.
Blastfurnace

1
Пов’язані відомості див. На C ++, зробленому легше: як ростуть вектори на сайті доктора Доббса.
jww

Відповіді:


262

Дві функції роблять дуже різні речі!

resize()Метод (і передаючи аргумент в конструктор еквівалентно) буде вставити або видалити відповідну кількість елементів в вектор , щоб зробити його заданий розмір (він має необов'язковий другий аргумент , щоб визначити їх значення). Це вплине на size(), ітерація перейде на всі ці елементи, push_back вставить після них, і ви можете безпосередньо отримати доступ до них за допомогою operator[].

reserve()Метод тільки виділяє пам'ять, але залишає його инициализирован. Це лише впливає capacity(), але size()буде незмінним. Немає значення для об’єктів, тому що у вектор не додається нічого. Якщо потім вставити елементи, перерозподіл не відбудеться, оскільки це було зроблено заздалегідь, але це єдиний ефект.

Тож це залежить від того, що ви хочете. Якщо ви хочете масив з 1000 елементів за замовчуванням, використовуйте resize(). Якщо ви хочете масив, до якого ви плануєте вставити 1000 елементів і хочете уникнути пари виділень, використовуйте reserve().

EDIT: Коментар Blastfurnace змусив мене прочитати запитання ще раз і зрозумів, що у вашому випадку правильної відповіді не вказувати вручну. Просто продовжуйте вставляти елементи в кінці, як вам потрібно. Вектор буде автоматично перерозподіляти по мірі необхідності і буде робити це більш ефективно , ніж ручний спосіб згадується. Єдиний випадок, коли reserve()є сенс, коли ви маєте досить точну оцінку загального розміру, який вам буде потрібно легко отримати заздалегідь.

EDIT2: Редагування рекламного питання: Якщо у вас є початкова оцінка, тоді reserve()ця оцінка. Якщо цього виявляється недостатньо, просто дозвольте вектору це зробити.


Я відредагував питання. У мене є певна оцінка для vector.
iammilind

3
@Jan: ну, це неміцно чи не відповідно до того, наскільки складно ви створили для себе підтримку необхідної властивості. Щось подібне x.reserve(x.size() + newdata); vector<int>::iterator special_element = get_special_element(x); for (int i = 0; i < newdata; ++i) { if some_function(i, special_element) x.push_back(i); }є досить міцним, що стосується резервування місця. Я не маю уявлення, скільки елементів насправді буде додано, але я маю верхню межу. Звичайно, коли ви сумніваєтесь, що з векторами ви можете просто використовувати індекси замість ітераторів, різниця зазвичай незначна.
Стів Джессоп

4
Ваше формулювання має сенс для того, щоб хтось уже знав правильну відповідь, але може легко ввести в оману людей, які потребують поставити питання. "resize () ... буде вставляти задану кількість елементів у вектор" - справедливо лише при першому використанні - воно, як правило, вставляє різницю між запитуваним числом і попереднім size(). "Метод резерву () виділяє лише пам'ять" - він може або не може виділяти пам'ять залежно від того, чи capacity()достатньо це вже, можливо, також знадобиться перемістити елементи та розмістити їх вихідну пам'ять. "хочу уникнути пари виділень" та копій тощо
Tony Delroy

19
Насправді резервування перед натисканням є життєво важливим і потрібно використовувати. Припустимо, що ви кодуєте якийсь 3d-завантажувач моделі, і модель має 15000 вершин. Якщо ви спробуєте відштовхувати кожну вершину під час завантаження, не попередньо розподіляючи їх, це займе серйозний час. Я особисто переживав це, я намагався завантажити модель .obj автомобіля з майже 100000 вершин, це зайняло 30 секунд. Потім я відновив код за допомогою попереднього розподілу за допомогою .reserve (), зараз це займає 3 секунди. Щойно введення. Резерву (100000) на початку коду заощадило 27 секунд.
деніз

1
@deniz Це банально істинно в масштабі 100000, але дуже не відповідає дійсності в масштабі 100-300, де резервування може бути марним, якщо робити це без потреби.
deworde

30

resize()не тільки виділяє пам'ять, вона також створює стільки примірників, скільки потрібний розмір, який ви передаєте в resize()якості аргументу. Але reserve()виділяє лише пам'ять, вона не створює екземплярів. Це є,

std::vector<int> v1;
v1.resize(1000); //allocation + instance creation
cout <<(v1.size() == 1000)<< endl;   //prints 1
cout <<(v1.capacity()==1000)<< endl; //prints 1

std::vector<int> v2;
v2.reserve(1000); //only allocation
cout <<(v2.size() == 1000)<< endl;   //prints 0
cout <<(v2.capacity()==1000)<< endl; //prints 1

Вихід ( демонстрація в Інтернеті ):

1
1
0
1

Тому resize()може бути не бажано, якщо ви не хочете об'єктів, створених за замовчуванням. Також буде повільно. Крім того, якщо ви push_back()додасте до нього нові елементи, size()вектор буде ще більше збільшуватися , виділяючи нову пам'ять (що також означає переміщення існуючих елементів у щойно виділений простір пам'яті). Якщо ви використали reserve()на початку, щоб переконатися, що вже достатньо виділеної пам'яті, size()вектор буде збільшуватися, коли ви перейдете push_back()до неї, але він знову не виділить нову пам'ять, поки не вичерпається місце, відведене для неї .


6
Після цього reserve(N)ми можемо operator []нешкідливо використовувати . правильно?
iammilind

2
Хоча більшість реалізацій виділять точну суму, яку ви запитуєте reserve, специфікація вимагає лише виділити принаймні стільки, тому деякі реалізації можуть закруглюватись до деякої межі і, таким чином, демонструвати більшу ємність, ніж 1000.
Ян Худек,

16
@iammilind: Ні, якщо індекс більший або дорівнює v.size(). Зверніть увагу, що вектор reserve(N)не змінюється size().
Наваз

5
@iammilind: неправильно. Після виклику RESERVE записів не додається, отримується лише достатньо пам'яті для їх додавання.
Ян Худек

2

З вашого опису виглядає так, що ви хочете "резервувати" виділений простір для зберігання векторних t_Names.

Візьміть до уваги, що resizeініціалізуйте щойно виділений вектор там, де reserveпросто виділяє, але не будує. Отже, "резерв" набагато швидший, ніж "розмір"

Ви можете посилатися на документацію щодо різниці розмірів і резерву


1
Будь ласка, зверніться сюди: вектор та ємність ( чому? )
1111

1
Дякую за додавання посилання, sehe
dip

2

зарезервуйте, коли ви не хочете, щоб об’єкти ініціалізувалися під час резервування. Крім того, ви можете віддати перевагу логічно диференціювати та відстежувати його кількість порівняно з кількістю використання при зміні розміру. тому в інтерфейсі є різниця в поведінці - вектор буде представляти однакову кількість елементів, коли зарезервовано, і буде на 100 елементів більшим, якщо змінити розмір у вашому сценарії.

Чи є кращий вибір у такому сценарії?

це повністю залежить від ваших цілей при боротьбі з поведінкою за замовчуванням. деякі люди віддають перевагу індивідуальним розподільникам, але нам справді потрібне краще уявлення про те, що саме ви намагаєтеся вирішити у своїй програмі, щоб проконсультувати вас.

fwiw, багато векторних реалізацій просто подвоїть кількість виділених елементів, коли вони повинні зростати - ви намагаєтеся мінімізувати пікові розміри розподілу чи намагаєтесь залишити достатньо місця для якоїсь безкоштовної програми блокування чи чогось іншого?


" резервуйте, коли ви не хочете, щоб об'єкти ініціалізувалися під час резервування. " Правильна формулювання - це коли ви не хочете, щоб об'єкти існували . Це не як неініціалізований масив тривіально сконструйованого типу, де об'єкти не можна читати, але їм можна призначити; скоріше, лише пам’ять зарезервована, але в ній немає об'єктів, тому до них неможливо отримати доступ operator[]чи що-небудь.
підкреслюй_d
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.