Чи гарантовано std :: векторні елементи суміжні?


111

Моє запитання просте: чи std :: векторні елементи гарантовано є суміжними? Як слово, чи можу я використовувати вказівник на перший елемент std :: vector як C-масив?

Якщо моя пам'ять служить мені добре, стандарт C ++ не давав такої гарантії. Однак вимоги std :: vector були такими, що їх було практично неможливо виконати, якщо елементи не були суміжними.

Хтось може це уточнити?

Приклад:

std::vector<int> values;
// ... fill up values

if( !values.empty() )
{
    int *array = &values[0];
    for( int i = 0; i < values.size(); ++i )
    {
        int v = array[i];
        // do something with 'v'
    }
}

Я знаю, що у вас виникають проблеми, якщо ви мутуєте valuesвсередині цього ifблоку. Я не знаю відповіді на ваше запитання, тому я просто залишаю коментар. :)
Грег Д

@Greg: Яка біда - ти можеш трішки допрацювати?
Reunanen

Я вважаю, що він мав на увазі, що натискання нових значень може викликати "realloc", що призведе до того, що масив стане недійсним.
Мартін Кот

Виклики, які мутують values, конкретно, що змінюють її розмір (наприклад, push_back()), можуть викликати перерозподіл базового вектора, який визнає недійсним скопійований покажчик array. За тим самим принципом використовується вектор :: ітератор замість вказівника у вектор. :)
Грег Д

1
Так, я поставив значення `` навколо, щоб спробувати зрозуміти, що я говорив про сам клас, а не про значення, що містяться в ньому. :) Невдале називання і все таке. Я не думаю, що це насправді проблема в загальному випадку, коли це питання є актуальним, хоча - чому хтось захопить вказівник на пам'ять, а потім почне спілкуватися з вектором, а не використовувати вказівник? Спритність.
Грег Д

Відповіді:


118

Це було пропущено відповідно до стандартних стандартів C ++ 98, але пізніше додано як частина TR. Наступний стандарт C ++ 0x, звичайно, буде містити це як вимогу.

Від n2798 (проект C ++ 0x):

23.2.6 Вектор шаблону класу [вектор]

1 Вектор є контейнером послідовностей, який підтримує ітератори випадкового доступу. Крім того, він підтримує (амортизовані) постійні операції вставки та стирання часу в кінці; вставлення та стирання в середині займає лінійний час. Управління зберіганням обробляється автоматично, хоча можна підказати для підвищення ефективності. Елементи вектора зберігаються безперервно, це означає, що якщо v - вектор, де T - якийсь тип, відмінний від bool, то він підкоряється ідентичності & v [n] == & v [0] + n для всіх 0 <= n <v .size ().


3
Це також зазначено в ISO 14882, 2-е видання: Розділ 23.2.4 [lib.vector]: "Елементи вектора зберігаються безперервно, це означає, що якщо v - вектор <T, Аллокатор>, де T є типом, відмінним від bool, тоді він підкоряється тотожності & v [n] == & v [0] + n для всіх 0 <= n <v.size (). "
Майк Карон

4
so s, TR, TC, :) Насправді C ++ 03 також називають C ++ 98-TC1 (технічне виправлення) з того, що я читав
Йоханнес Шауб - ліб

2
А як щодо векторів векторів? Інерські вектори одразу після внутрішніх векторів останньої групи?
huseyin tugrul buyukisik

1
@huseyin tugrul buyukisik Ви дізналися відповідь на це? Мені також цікаво, як це працює
Девід Дорія

1
@huseyin tugrul buyukisik Звичайно, це правда, але випадки подальшого std::vectorє суміжними. Наприклад.: В std::vector<std::vector<int>> vелементах v[0], v[1]... зберігаються згодом у пам'яті, але елемент v[0].back()і v[1].front()не гарантовано.
jarzec

27

Як вказували інші відповіді, вміст вектора гарантовано є безперервним (крім дивацтва булів).

Зауваження, яке я хотів додати, полягає в тому, що якщо ви зробите вставку або видалення вектора, що може призвести до перерозподілу вектора в пам'яті, тоді ви приведете до недійсності всіх збережених вказівників та ітераторів.


1
Елементи все ще зберігатимуться в суміжному блоці пам'яті, він би просто знаходився в іншому місці. Питання стосувалося конкретно питання про примикання.
Діма

2
Але існуючі вказівники та ітератори будуть визнані недійсними.
Білл Лінч

Гарна думка. Ви повинні вкласти це у свою відповідь, щоб уточнити, що ви маєте на увазі.
Діма


Тепер я знаю, чому вчора моя програма була сегментованою, коли я прокрутив її в подвійній петлі, видаляючи певні елементи :) Дякую!
користувач2891462

9

Насправді стандарт гарантує, що a vectorє безперервним у пам'яті і що &a[0]може передаватися aC функції, яка очікує масив.

Виняток із цього правила полягає vector<bool>лише в boolтому, що він використовує лише один біт на кожне, хоча він має постійну пам'ять, але його не можна використовувати як bool*(це широко вважається помилковою оптимізацією та помилкою).

BTW, чому ви не використовуєте ітератори? Ось для чого вони.


1
> BTW, чому ви не використовуєте ітератори? Ось для чого вони. Можливо, він прочитав новий документ Алексанреску на тему: boostcon.com/site-media/var/sphene/sphwiki/attachment/2009/05/…
Неманя Трифунович

Дякую за посилання, я перейду до свого списку читання (намагаюся не пропустити статті Олександресу)
Motti

Мвахаха, всі, здається, сьогодні говорять про цю презентацію. Подивіться, дискусія про це ще гаряча: groups.google.com/group/comp.lang.c++.moderated/browse_thread/…
Йоханнес Шауб - ліб

Якщо ви уважно його прочитали, стаття Олександреску насправді не говорить "Не використовуйте ітераторів у C ++", а в ньому написано "Перевірте D". Підхід, який він описує в цій роботі, надзвичайно схожий на будь-які існуючі мови та рамки, які поглинули функціональну спадщину (Список, Схема, Хаскелл), і я серйозно сумніваюся, чи ще один синтаксис, що базується на С, є ідеальною відправною точкою для кращого обробка списку. Минулого року я ненадовго спробував переконати його перетворити свої значні таланти на вдосконалення вже усталеної мови на зразок C #, але я не боюся успіху! :)
Даніель Ервікер

6

Як вже говорили інші, vector внутрішньо використовується суміжний масив об'єктів. Покажчики на цей масив слід трактувати як недійсні, коли будь-яка функція, що не входить у const, називається IIRC.

Однак є виняток !!

vector<bool>має спеціалізовану реалізацію, розроблену для економії місця, так що кожен bool використовує лише один біт. Базовий масив не є суміжним масивом bool, а арифметика масиву на vector<bool>не працює так, як vector<T>хотілося б.

(Я припускаю, що також можливо, що це стосується будь-якої спеціалізації вектора, оскільки ми завжди можемо реалізувати нову. Однак std::vector<bool>це єдина, помилкова, стандартна спеціалізація, на якій проста арифметика вказівника не працюватиме.)


Користувач не має права спеціалізуватися std::vector, а всі інші вектори повинні використовувати суміжні сховища. Тому, на std::vector<bool>щастя, це єдиний дивний стандартний вектор. (Я твердо переконаний, що цю спеціалізацію слід припинити і замінити, наприклад, std::dynamic_bitsetз майже такою ж функціональністю. Це не погана структура даних, це просто не вектор.)
Arne Vogel,

3

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

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

void generate(std::vector<float> v)
{
  float f = generate_next_float();
  v.push_back(f);
}

Тепер я можу передавати поплавці вектора як масив функціям, пов’язаним з буфером OpenGL. Це також усуває необхідність розміруof для визначення довжини масиву.

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


2
ця функція не має для мене ніякого сенсу. ти маєш на увазі передавати посилання або вказівник на себе, vа не на vсебе? тому що проходження vпоодинці призведе до того, що всередині функції буде зроблена копія, яка припинить своє існування після закінчення функції. Таким чином, ви натискаєте щось на вектор лише для видалення вектора, коли функція закінчується.
johnbakers

1

cplusplus.com:

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


1

Так, елементи std :: vector гарантовано є суміжними.


Правильно. Я думаю, я використовую занадто багато таких :)
Benoît
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.