Arhn - архитектура программирования

расположение векторных указателей гарантировано?

Предположим, у меня есть вектор целых чисел,

std::vector<int> numbers;

который заполнен кучей значений, тогда я говорю сделать это (где запись существует в 43)

int *oneNumber = &numbers[43];

Гарантируется ли, что oneNumber всегда указывает на int с индексом 43, даже если, скажем, я изменяю размер чисел на что-то вроде number.resize(46)?

Я не уверен на 100%, какое ожидаемое поведение здесь, я знаю, что векторы гарантированно будут смежными, но не уверен, что эта непрерывность также будет означать, что все индексы в векторе останутся в одном и том же месте на протяжении всей его жизни.

05.01.2012

  • По этой причине я всегда опасаюсь создавать вектор типов значений, потому что мне неизбежно нужен указатель на объекты внутри вектора. Поэтому я сохраняю ссылку типа int* ref = &numbers[43].. позже делаю пару push_backs в numbers и WHAMMO! Ошибка: ref недействителен, потому что numbers был полностью перераспределен и сохранен в совершенно другом пространстве памяти. 05.01.2013
  • @bobobobo Совершенно верно. Хотя для ситуаций, когда количество элементов известно во время создания (и нет, я не могу использовать std::array, потому что я emplace конструирую значения с помощью цикла), reserve гарантирует, что все указатели/итераторы в векторе останутся действительными до тех пор, пока поскольку его размер не превышает его текущей емкости (что может вызвать перераспределение и, следовательно, перемещение адресов содержащихся значений). Это спасло меня несколько раз, но каждый последующий раз я забываю reserve и некоторое время чешу голову, пока не вспомню. :D 12.01.2016

Ответы:


1

Гарантируется ли, что oneNumber всегда указывает на int с индексом 43?

Да, это гарантируется стандартом.

даже если сказать, что я изменяю размер чисел на что-то вроде number.resize(46)?

Нет. Как только вы измените размер, добавите или удалите что-либо в векторе, все адреса и итераторы к нему станут недействительными. Это связано с тем, что может потребоваться перераспределение вектора с новыми ячейками памяти.

05.01.2012
  • А если использовать std::list? Поможет ли это обойти проблему неверного адреса, если вы будете использовать только вызовы list.push_back() для добавления элементов? (и вы не удаляете элемент, на который указывает oneNumber? 29.09.2012
  • @bobobobo Хороший вопрос. Я не уверен, что об этом говорит стандарт, но я не вижу причин, по которым добавление/удаление std::list сделало бы недействительными любые указатели. (кроме удаленного элемента) Мы обсуждаем это в гостиной прямо сейчас, если вы хотите присоединиться. 29.09.2012
  • Нашел еще один обходной путь, который я сейчас использую. 07.10.2012

  • 2

    Ваша паранойя правильная. Изменение размера std::vector может привести к изменению его местоположения в памяти. Это означает, что ваш oneNumber теперь указывает на старую область памяти, которая была освобождена, и поэтому доступ к ней является неопределенным поведением.

    05.01.2012

    3

    Указатели, ссылки и итераторы на элементы std::vector гарантированно останутся на месте до тех пор, пока вы добавляете только std::vector и размер std::vector не превышает capacity() в момент указателя, ссылки , или был получен итератор. Как только его размер изменится за пределы capacity(), все указатели, ссылки и итераторы на этот std::vector станут недействительными. Обратите внимание, что вещи также становятся недействительными при вставке в другое место, кроме конца std::vector.

    Если вы хотите, чтобы ваши объекты оставались на месте, и вы вставляете новые элементы только в конце или в начале, вы можете использовать std::deque. Указатели и ссылки на элементы в std::deque становятся недействительными только при вставке в середину std::deque или при удалении из середины или при удалении ссылочного объекта. Обратите внимание, что итераторы к элементам в std::deque становятся недействительными каждый раз, когда вы вставляете элемент в std::deque или удаляете из него какой-либо элемент.

    05.01.2012

    4

    Как говорили все остальные, когда вы вызываете .resize() для вектора, ваши указатели становятся недействительными, потому что (старый массив) может быть полностью освобожден, а совершенно новый может быть перераспределен и ваши данные скопированы в него.

    Одним из обходных путей для этого является не хранить указатели в векторе STL. Вместо этого сохраняйте целочисленные индексы.

    Итак, в вашем примере

    std::vector<int> numbers;
    int *oneNumber = &numbers[43]; // no. pointers invalidated after .resize or possibly .push_back.
    int oneNumberIndex = 43 ;      // yes. indices remain valid through .resize/.push_back
    
    07.10.2012

    5

    Нет — вектор можно перераспределить, когда он вырастет. Обычно после того, как вектор удваивается в размере.

    Из стандарта C++11

    1 Remarks: Causes reallocation if the new size is greater than the old capacity. If no
    reallocation happens, all the iterators and references before the insertion point
    remain valid. If an exception is thrown other than by the copy constructor, move 
    constructor, assignment operator, or move assignment operator of T or by any 
    InputIterator operation there are no effects. If an exception is thrown by the move 
    constructor of a non-CopyInsertable T, the effects are unspecified.
    
    05.01.2012

    6

    Когда вы используете векторную функцию resize() или backup() для увеличения емкости вектора, может потребоваться перераспределение памяти для поддержки массива. В случае перераспределения новая память не будет располагаться по тому же адресу, поэтому адрес, хранящийся в oneNumber, больше не будет указывать на нужное место.

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

    05.01.2012

    7

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

    05.01.2012
    Новые материалы

    Коллекции публикаций по глубокому обучению
    Последние пару месяцев я создавал коллекции последних академических публикаций по различным подполям глубокого обучения в моем блоге https://amundtveit.com - эта публикация дает обзор 25..

    Представляем: Pepita
    Фреймворк JavaScript с открытым исходным кодом Я знаю, что недостатка в фреймворках JavaScript нет. Но я просто не мог остановиться. Я хотел написать что-то сам, со своими собственными..

    Советы по коду Laravel #2
    1-) Найти // You can specify the columns you need // in when you use the find method on a model User::find(‘id’, [‘email’,’name’]); // You can increment or decrement // a field in..

    Работа с временными рядами спутниковых изображений, часть 3 (аналитика данных)
    Анализ временных рядов спутниковых изображений для данных наблюдений за большой Землей (arXiv) Автор: Рольф Симоэс , Жильберто Камара , Жильберто Кейрос , Фелипе Соуза , Педро Р. Андраде ,..

    3 способа решить квадратное уравнение (3-й мой любимый) -
    1. Методом факторизации — 2. Используя квадратичную формулу — 3. Заполнив квадрат — Давайте поймем это, решив это простое уравнение: Мы пытаемся сделать LHS,..

    Создание VR-миров с A-Frame
    Виртуальная реальность (и дополненная реальность) стали главными модными терминами в образовательных технологиях. С недорогими VR-гарнитурами, такими как Google Cardboard , и использованием..

    Демистификация рекурсии
    КОДЕКС Демистификация рекурсии Упрощенная концепция ошеломляющей О чем весь этот шум? Рекурсия, кажется, единственная тема, от которой у каждого начинающего студента-информатика..