Форум программистов, компьютерный форум, киберфорум
С++ для начинающих
Войти
Регистрация
Восстановить пароль
Блоги Сообщество Поиск Заказать работу  
 
 
Рейтинг 4.91/11: Рейтинг темы: голосов - 11, средняя оценка - 4.91
1 / 1 / 0
Регистрация: 21.11.2017
Сообщений: 24

Работа с std::vector содержащим указатели

21.11.2017, 12:27. Показов 2540. Ответов 49
Метки нет (Все метки)

Студворк — интернет-сервис помощи студентам
Привет! Я долгое время находился в ридонли, решил всё же задать вопрос, так как проблему не получается решить самостоятельно, из-за нехватки знаний по всей видимости. Оговорюсь, что на момент как я решил выучить c++, я уже хорошо знал несколько языков в том числе python java php и так далее.

Пишу небольшую игру на С++, кодовая база получилась уже сейчас довольно большая (для меня как только-только изучающего с++). Всё бы хорошо кроме одного момента.

В программе есть вектор содержащий все сущности игры вида:
C++
1
std::vector<Entity *> items
То есть любой объект наследует в конечном счёте Entity. Рендеринг каждого фрейма происходит путём обхода всех сущностей
и вызова у каждой из них ->update(), всё в принципе стандартно. Всё работает хорошо и как нужно кроме одного момента:

Периодически абсолютно в неясные для меня моменты (то есть какой либо зависимости у этих событий нет) получаю segfault либо на ->update(); (если я компилирую в g++) либо на сортировке объектов, в случае добавления новых сущностей для сохранения порядка отрисовки (если компилирую в msvc). segfault вызванный тем, что я пытаюсь вызвать метод объекта из вектора, при том что указатель ссылается на чистую область памяти (то есть объекта там уже нет)

Разработку веду в clion (gcс + mingw) И периодами сверяюсь с msvc (там удобный профайлер, да и в целом компилятор по умолчанию более строгий).

Понятно что весь код внутри update, я физически привести не смогу, вопрос вот в чем, может быть есть какие-то методики для понимания кто и в каком месте заменяет на heap'e реальные объекты таким образом, что указатель ведёт на чистую область памяти где не содержится изначально искомого объекта? Либо же я просто совершаю некую распространенную ошибку новичка и каким-то не правильным образом работаю с вектором таким образом что при наступлении определенных событий происходит segfault? Сразу оговорюсь, resize() у вектора я сам не вызываю, то есть аллокация новых элементов происходит автоматом и происходит успешно до какого-то момента.

Так же я могу привести кусок кода на котором возникает (в случае неопределенных обстоятельств) segfault при gcc компиляции.

C++
1
2
3
4
5
6
7
8
9
10
void sort() {
        std::sort(items.begin(), items.end(), [](Entity *a, Entity *b) -> bool {
            if (a->getDrawOrder() == b->getDrawOrder()) {
                return a->getDrawOrder() + a->getWorldCoordinates().x + a->getWorldCoordinates().y <
                       b->getDrawOrder() + b->getWorldCoordinates().x + b->getWorldCoordinates().y;
            } else {
                return a->getDrawOrder() < b->getDrawOrder();
            }
        });
    }
0
IT_Exp
Эксперт
34794 / 4073 / 2104
Регистрация: 17.06.2006
Сообщений: 32,602
Блог
21.11.2017, 12:27
Ответы с готовыми решениями:

На основе исходного std::vector<std::string> содержащего числа, создать std::vector<int> с этими же числами
подскажите есть вот такая задача. Есть список . Создать второй список, в котором будут все эти же числа, но не в виде строк, а в виде...

Как передать целочисленную матрицу типа std::vector<std::vector<int> > в функцию?
Здравствуйте. Почитал на форуме, но так и не понял что я делаю не так. Имеется двумерный вектор. Размера .. Нужно его передать в...

Вывести значения std::vector<std::vector<int*> >
Подскажите, как вывести значения? const size_t row = 3; const size_t col = 3; std::vector&lt;std::vector&lt;int*&gt; &gt; imatrix; ...

49
309 / 221 / 74
Регистрация: 23.05.2011
Сообщений: 981
21.11.2017, 17:15
Студворк — интернет-сервис помощи студентам


Окей, тогда попробуй переписать цикл:
C++
1
2
3
4
5
for(size_t i = 0; i<items.size(); ++i)
{
    Entity * element = items[i];
    element->update();
}
0
1 / 1 / 0
Регистрация: 21.11.2017
Сообщений: 24
21.11.2017, 17:15  [ТС]
Цитата Сообщение от Operok Посмотреть сообщение
Извиняюсь, забыл что update у вас скорее всего виртуальный, тогда падение происходит при попытке вытащить адрес нужного метода из "таблицы виртуальных методов".
Именно так, update объявлен virtual
0
183 / 181 / 66
Регистрация: 15.02.2015
Сообщений: 515
21.11.2017, 17:25
mrAndersen7, понимаю что глупо, но всё же, не изменяете-ли вы косвенно или напрямую вектор items из метода update?
0
1 / 1 / 0
Регистрация: 21.11.2017
Сообщений: 24
21.11.2017, 17:37  [ТС]
Цитата Сообщение от Operok Посмотреть сообщение
mrAndersen7, понимаю что глупо, но всё же, не изменяете-ли вы косвенно или напрямую вектор items из метода update?
Кроме как вставкой из конструктора объектов типо Entity и его наследников, о которой писал выше никак. Сами элементы содержащиеся внутри items, конечно же подвергаются изменениям через доступ по указателю "->" оператор (это кстати как я понял причина, по которой я не смогу использовать set)

Добавлено через 1 минуту
Цитата Сообщение от New man Посмотреть сообщение
Окей, тогда попробуй переписать цикл:
Переписал, проблема осталась
0
309 / 221 / 74
Регистрация: 23.05.2011
Сообщений: 981
21.11.2017, 17:45
При добавлении элементов, итераторы вектора могут стать невалидными.
Вот пор push_back:
Iterator validity
If a reallocation happens, all iterators, pointers and references related to the container are invalidated.
Otherwise, only the end iterator is invalidated, and all iterators, pointers and references to elements are guaranteed to keep referring to the same elements they were referring to before the call.
То есть, это происходит, но не всегда.

Рекомендую попробовать сделать так, как я писал выше, только чуть изменив:
C++
1
2
3
4
5
6
7
8
// Все новые добавленные элементы будут с индексами >= old_size
// Соответственно, мы на этом цикле обновления не будем запускать их update.
const size_t old_size = items.size(); 
for(size_t i = 0; i< old_size ; ++i)
{
    Entity * element = items[i];
    element->update();
}
Добавлено через 2 минуты
А новые значения element в моём цикле тоже принимают эти "странные значения"?
0
1 / 1 / 0
Регистрация: 21.11.2017
Сообщений: 24
21.11.2017, 17:59  [ТС]
Цитата Сообщение от New man Посмотреть сообщение
екомендую попробовать сделать так, как я писал выше, только чуть изменив:
Переписал с ограничем по индексу (old_size) ситуация такая же. Только теперь она возникает по-моему чаще не на update непосредственно, а успевает дойти до сортировки в результате добавления нового элемента (Entity) (этот код я приводил был выше)

C++
1
2
3
4
5
6
7
8
9
10
void sort() {
        std::sort(items.begin(), items.end(), [](Entity *&a, Entity *&b) -> bool {
            if (a->getDrawOrder() == b->getDrawOrder()) {
                return a->getDrawOrder() + a->getWorldCoordinates().x + a->getWorldCoordinates().y <
                       b->getDrawOrder() + b->getWorldCoordinates().x + b->getWorldCoordinates().y;
            } else {
                return a->getDrawOrder() < b->getDrawOrder();
            }
        });
    }
И здесь снова такая же ситуация, то есть в данном случае в a был подан непонятный адрес на несуществующий объект, а в b - нормальный указатель, и при попытке вызвать drawOrder он и ломается.

Но вот сортировка может быть вызвана и в середине цикла итерации.
0
309 / 221 / 74
Регистрация: 23.05.2011
Сообщений: 981
21.11.2017, 18:05
C++
1
(Entity *&a, Entity *&b)
Нет смысла передавать тут по ссылке. Сделай (Entity *&a, Entity *&b).

Добавлено через 1 минуту
И вызывай сортировку после цикла update
Потому что это точно портит итераторы, да и мою идею с индексами тоже.
И это очень неоптимально.
0
1 / 1 / 0
Регистрация: 21.11.2017
Сообщений: 24
21.11.2017, 18:07  [ТС]
Цитата Сообщение от New man Посмотреть сообщение
Нет смысла передавать тут по ссылке. Сделай (Entity *&a, Entity *&b).
Это сделал, более того сортировку переделал на конец фрейма через флаг. То есть после прохода всех update только делается сортировка один раз за фрейм. Всё равно тоже самое
0
309 / 221 / 74
Регистрация: 23.05.2011
Сообщений: 981
21.11.2017, 18:09
Пожалуй, залей куда-нибудь на github проект, тут уже так не разобраться.
0
1 / 1 / 0
Регистрация: 21.11.2017
Сообщений: 24
21.11.2017, 20:10  [ТС]
https://pastebin.com/qsLpw41P - Менеджер сущностей

Добавлено через 1 час 48 минут
https://pastebin.com/qsLpw41P - Менеджер сущностей
Соответственно refreshEntities выполняется каждый фрейм
0
зомбяк
 Аватар для TRam_
1585 / 1219 / 345
Регистрация: 14.05.2017
Сообщений: 3,940
21.11.2017, 20:32
Ошибка в этом
C++
1
2
3
4
5
6
7
8
9
10
11
12
            for (auto &e:itemsToRemove) {
                //remove if item presents in group
                if (e->getGroupName() != "~") {
                    removeFromGroup(e->getGroupName(), e);
                }
 
                //remove from actual vector
                items.erase(std::remove(items.begin(), items.end(), e), items.end());
 
                //free object
                delete e;
            }
Нужно удалять не e, которая является итератором массива, а скорее *e, в которой указатель. Хотя могу ошибаться.

Добавлено через 3 минуты
Да, ошибся. e тут не итератор, а просто ссылка.
0
1 / 1 / 0
Регистрация: 21.11.2017
Сообщений: 24
21.11.2017, 20:36  [ТС]
Да, ошибся. e тут не итератор, а просто ссылка.
Да, тут просто ссылка. В тред призываются гуру, сил уже нет дебажить это)
0
зомбяк
 Аватар для TRam_
1585 / 1219 / 345
Регистрация: 14.05.2017
Сообщений: 3,940
22.11.2017, 04:42
Возможно у Entity (и дочерних классов) не виртуальный деструктор, потому не доудаляются...

Добавлено через 3 часа 3 минуты
И ещё добавить проверку на дублирование элементов при удалении
C++
1
2
3
4
void remove(Entity *item) {
    if(std::find(itemsToRemove.cbegin(), itemsToRemove.cend(), item) == itemsToRemove.cend())
        itemsToRemove.push_back(item);
}
0
309 / 221 / 74
Регистрация: 23.05.2011
Сообщений: 981
22.11.2017, 09:36
Скинь вообще весь проект для MS VS, попробую подебажить.
0
183 / 181 / 66
Регистрация: 15.02.2015
Сообщений: 515
22.11.2017, 10:13
Цитата Сообщение от TRam_ Посмотреть сообщение
Возможно у Entity (и дочерних классов) не виртуальный деструктор, потому не доудаляются...
это никак не объясняет segfault при обходе вектора и при сортировке.
Цитата Сообщение от TRam_ Посмотреть сообщение
И ещё добавить проверку на дублирование элементов при удалении
верное замечание, но результатом такого дублирование было бы вызов delete к уже освобождённой "памяти".

Тут, похоже что во время выполнения той или иной операции, будь она в цикле или в лямбде (касательно sort), текущий итератор (хоть его и не видно, но он есть) становится невалидный, а произойти это может если в одном или нескольких методах из: getDrawOrder, getWorldCoordinates или update - происходит изменение вектора items, например создаётся один или несколько объектов, которые в конструкторе себя добавляют в items.
Как бы инвалидация итератора в "renge base for" цикле может быть только в результате каких-либо действий в теле этого цикла (исключая многопоточность).
0
1 / 1 / 0
Регистрация: 21.11.2017
Сообщений: 24
22.11.2017, 11:55  [ТС]
Цитата Сообщение от Operok Посмотреть сообщение
верное замечание, но результатом такого дублирование было бы вызов delete к уже освобождённой "памяти".
Проверку сделал, она в общем-то ни на что не повлияла, поскольку там delete верно отрабатывал.

Цитата Сообщение от Operok Посмотреть сообщение
Тут, похоже что во время выполнения той или иной операции, будь она в цикле или в лямбде (касательно sort), текущий итератор (хоть его и не видно, но он есть) становится невалидный, а произойти это может если в одном или нескольких методах из: getDrawOrder, getWorldCoordinates или update - происходит изменение вектора items, например создаётся один или несколько объектов, которые в конструкторе себя добавляют в items.
Ну код тут очень тривиальный.

C++
1
2
3
4
5
6
const sf::Vector2f &Entity::getWorldCoordinates() const {
    return worldCoordinates;
}
int Entity::getDrawOrder() const {
    return drawOrder;
}
Внутри update естественно происходит изменения вектора (потому что добавление элемента и создание его на куче через new происходит в том числе и внутри update). Но уж никак не в getDrawOrder и getWorldCoordinates которые вызываются на сортировке. То есть если смотреть на деббагер, то внтрь лямбды уже были поданы невалидные указатели, то есть это как раз по всей видимости проблема не валидного итератора. Но парадокс в том, что сортировка то теперь делается один раз в конце фрейма, и там тончо уже все update отработали. Так все эти проблемы - они возникают непонятно каким образом, то есть я могу дебажить 5 минут и никакой проблемы не будет, а может быть сразу сегфалт

Цитата Сообщение от New man Посмотреть сообщение
Скинь вообще весь проект для MS VS, попробую подебажить.
Там супер много кода, я как раз заливаю на гитхаб, отпишу как залью, и если проблема не пропадет, я еще пару оптимизаций думаю сделать
0
зомбяк
 Аватар для TRam_
1585 / 1219 / 345
Регистрация: 14.05.2017
Сообщений: 3,940
22.11.2017, 12:33
Цитата Сообщение от Operok Посмотреть сообщение
Тут, похоже что во время выполнения той или иной операции, будь она в цикле или в лямбде (касательно sort), текущий итератор (хоть его и не видно, но он есть) становится невалидный
собирал код на основе Работа с std::vector содержащим указатели , убрав всё лишнее и сделав update() виртуальным.

При тестировании с полуслучайным добавлением-удалением, если отключить всю работу с itemsByGroup, SEGFAULT вылетало из-за дублирования элементов в itemsToRemove, когда для адреса элемента, к которому уже применялось delete e , повторно проходит итератор.

Но это не единственная остановка, иногда останавливалось на вот этом - Ntdll!DbgBreakPoint signal-received - остановка выполнения - и с чем это связано, не понимаю. После продолжения программа успешно выполнялась.

Если нет уверенности, что в void remove(Entity *item) попадают валидные адреса, теоретически можно сделать так:
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
            for (auto &e:itemsToRemove) {
 
                auto distance = std::distance(items.begin(), std::remove(items.begin(), items.end(), e));
 
                if((distance+1) == items.size()) {
 
                    //remove if item presents in group
                    if (e->getGroupName() != "~") {
                        removeFromGroup(e->getGroupName(), e);
                    }
 
                    //remove from actual vector
                    items.resize(distance);
 
                    //free object
                    delete e;
                }
            }
0
183 / 181 / 66
Регистрация: 15.02.2015
Сообщений: 515
22.11.2017, 14:23
Цитата Сообщение от mrAndersen7 Посмотреть сообщение
Внутри update естественно происходит изменения вектора
Очень не рекомендуется так делать (менять вектор в теле цикла, которым мы по этому вектору проходим), при добавлении нового элемента в вектор, он может реаллоцироваться, таким образом все его ранее созданные итераторы точно будут невалидными. В довесок вы при добавлении нового объекта делаете сортировку, что опять скорее всего испортит итераторы (не те что используются в сортировке). Это всё что касается update в цикле. По поводу вылета при сортировке (а на скрине видно, что адреса в принципе валидные, т.е. это не мусор), то возможно товарищ TRam_ нашёл ошибку (пост выше).

Добавлено через 5 минут
Вот грубый пример того, как можно поймать sigsegv при невалидном итераторе: http://rextester.com/EYM23196
если в коде убрать один update() в main, то ошибки уже не будет.
0
зомбяк
 Аватар для TRam_
1585 / 1219 / 345
Регистрация: 14.05.2017
Сообщений: 3,940
22.11.2017, 15:08
Цитата Сообщение от Operok Посмотреть сообщение
Вот грубый пример того, как можно поймать sigsegv при невалидном итераторе:
и который можно исправить как раз через старый-добрый for.
C++
1
2
3
4
5
void update()
{
    for(auto i = 0; i < aaa.size(); i++)
        aaa[i]->update();   
}
потому что в aaa.size() и aaa[i] значения сохранят актуальность даже после реаллокации вектора aaa, в отличии от итераторов или ссылок в новой форме записи for. Хотя конечно будет проигрыш по производительности.
0
1 / 1 / 0
Регистрация: 21.11.2017
Сообщений: 24
22.11.2017, 15:52  [ТС]
Закомментил сортировку в принципе, для теста. Проблема пропала как таковая. Сейчас попробую добавление элементов класть в очередь (новый вектор) и суммировать вектора в конце фрейма

Добавлено через 36 минут
В общем ситуация такая. Было изначально 2 места где был segfault на update() и на сортировке.
В том месте где был update - он возникал, потому что была инвалидация итератора, по причине добавления в вектор новых сущностей, она решилась таким образом (как было предложено выше и на предыдущей странице):

C++
1
2
3
4
5
6
7
8
int startSize = items.size();
 
        for (int i = 0; i < startSize; ++i) {
            Entity *e = items[i];
            e->update();
        }
 
        int endSize = items.size();
То есть обновляем только те элементы, которые присутствовали в векторе на момент вызова обновлений.

Проблема с segfault на сортировке не решилась, даже при том условии что происходит она только после всех операций update и итерации по вектору, таким вот образом:

C++
1
2
3
4
5
if (sortNextFrame && startSize == endSize) {
            sort();
            System::debugCounters["sort_operations"]++;
            sortNextFrame = false;
        }
Более того, сортировка здесь происходит только при отсутствии изменения размера вектора, всё равно в лямбду подаются непонятные адреса..

Добавлено через 2 минуты
UPD: Проблема с segfault на сортировке присутсвтует только при компиляции через mingw + gcc, при комплияции через MSVC - проблемы нет, перепроверил несколько раз
0
Надоела реклама? Зарегистрируйтесь и она исчезнет полностью.
BasicMan
Эксперт
29316 / 5623 / 2384
Регистрация: 17.02.2009
Сообщений: 30,364
Блог
22.11.2017, 15:52
Помогаю со студенческими работами здесь

Как изменять размер std::vector<std::vector>?
Здравствуйте, как нужно изменять размер std::vector&lt;std::vector&gt; например: std::vector&lt;std::vector&lt;float&gt;&gt; data; ...

Std::vector<std::pair<std::vector<int>::iterator, std::vector<int>::iterator>
Вопрос по вектору. Допустим есть вектор, std::vector&lt;int&gt; vec; на каком - то этапе заполнения я ставлю закладку итератора, ...

Std::vector/QVector в классе или std::vector/QVector классов?
Доброе время суток! Собственно вопрос в самой теме, есть некий класс class WorkJornal { private: string manager; ...

Реализация класса MyString. Стандартная библиотека, std::string, std::vector
как добавить реализацию конкатенации строк через перегрузку оператора &quot;+=&quot; в классе MyString и почему ошибка выдается???#include...

Передача функции указатель на элемент std::vector<std::string>
Доброй ночи тем, кому не спится (или живет в другом часовом поясе:p)! Есть функция, требующая в качестве параметра указатель на...


Искать еще темы с ответами

Или воспользуйтесь поиском по форуму:
40
Ответ Создать тему
Новые блоги и статьи
Очистка реквизитов документа при копировании
Maks 09.04.2026
Алгоритм из решения ниже применим как для типовых, так и для нетиповых документов на самых различных конфигурациях. Задача: при копировании документа очищать определенные реквизиты и табличную. . .
модель ЗдравоСохранения 8. Подготовка к разному выполнению заданий
anaschu 08.04.2026
https:/ / github. com/ shumilovas/ med2. git main ветка * содержимое блока дэлэй из старой модели теперь внутри зайца новой модели 8ATzM_2aurI
Блокировка документа от изменений, если он открыт у другого пользователя
Maks 08.04.2026
Алгоритм из решения ниже реализован на примере нетипового документа, разработанного в конфигурации КА2. Задача: запретить редактирование документа, если он открыт у другого пользователя. / / . . .
Система безопасности+живучести для сервера-слоя интернета (сети). Двойная привязка.
Hrethgir 08.04.2026
Далее были размышления о системе безопасности. Сообщения с наклонным текстом - мои. А как нам будет можно проверить, что ссылка наша, а не подделана хулиганами, которая выбросит на другую ветку и. . .
Модель ЗдрввоСохранения 7: больше работников, больше ресурсов.
anaschu 08.04.2026
работников и заданий может быть сколько угодно, но настроено всё так, что используется пока что только 20% kYBz3eJf3jQ
Дальние перспективы сервера - слоя сети с космологическим дизайном интефейса карты и логики.
Hrethgir 07.04.2026
Дальнейшее ближайшее планирование вывело к размышлениям над дальними перспективами. И вот тут может быть даже будут нужны оценки специалистов, так как в дальних перспективах всё может очень сильно. . .
Горе от ума
kumehtar 07.04.2026
Эта мне ментальная установка, что вот прямо сейчас, мол, мне для полного счастья не хватает (нужное вписать), и когда я этого достигну - тогда и полный кайф. Одна из самых сильных ловушек на пути. . . .
Использование значений реквизитов справочника в документе, с определенными условиями и правами
Maks 07.04.2026
1. Контроль срока действия договора Алгоритм из решения ниже реализован на примере нетипового документа "ЗаявкаНаРаботу", разработанного в конфигурации КА2. Задача: уведомлять пользователя, если. . .
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin
Copyright ©2000 - 2026, CyberForum.ru