C++: вектор указателей на объекты из другого вектора

У меня есть два класса, похожие на это:

class A
{
public:
    B* ptr1;
}

class B
{
public:
    std::vector<A*> list;
}

В основной реализации я делаю что-то вроде этого:

int main() {

// there are a lot more A objects than B objects, i.e. listOfA.size() >>> listOfB.size()
std::vector<A> listOfA;
std::vector<B> listOfB; 

while (//some loop)
{
    listOfB[jj].list.push_back( &(listofA[ii]) );
    listOfA[ii].ptr1 = &( listOfB[jj] );
}

} // int main end

В основном что-то вроде этого. Многие объекты A назначаются одному объекту B, и эти объекты A хранятся в этом векторе указателей как указатели. Кроме того, каждый из этих объектов A получает указатель на объект B, которому они принадлежат. Для контекста я в основном использую алгоритм подключенных компонентов с кодированием длин серий (для сегментации изображения), где класс A — это линейные сегменты, а класс B — конечные объекты на изображении.

Таким образом, все указатели вектора в классе B указывают на объекты, которые хранятся в обычном векторе. Эти объекты должны быть удалены, когда обычный вектор выходит за рамки, верно? Я читал, что вектор указателя, как в классе B, обычно требует написания ручного деструктора, но я думаю, что здесь этого быть не должно...

Причина, по которой я спрашиваю, конечно же, потому что мой код продолжает падать. Я использую камеру Asus Xtion Pro для получения изображений, а затем выполняю алгоритм для каждого изображения. Странно то, что программа вылетает всякий раз, когда я чуть сильнее встряхиваю камеру. Когда камера неподвижна или перемещается незначительно или медленно, ничего не происходит. Также при использовании другого алгоритма (тоже связанные компоненты, но без run-length-encoding и тоже не использующего указатели) ничего не вылетает, сколько бы я не тряс камеру. Кроме того, в режиме отладки (который работал намного медленнее, чем в режиме выпуска) тоже ничего не зависало.

Я попытался создать деструктор для вектора указателя в классе B, но это привело к ошибке «блок действителен», поэтому я предполагаю, что он удалил что-то дважды. Я также пытался заменить каждый указатель на С++ 11 std::shared_ptr, но это приводило только к очень нерегулярному поведению, и код все еще зависал, когда я встряхивал камеру.

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

РЕДАКТИРОВАНИЕ (РЕШЕНО): Решение (см. принятый ответ) состояло в том, чтобы гарантировать, что размер вектора listOfB не изменится во время выполнения, например, с помощью резервирования () для резервирования достаточно места для него. После этого все заработало нормально! По-видимому, это сработало, потому что, если размер вектора 'listOfB' изменяется (с помощью push_back()), адреса внутренней памяти экземпляров B в нем также изменяются, в результате чего указатели в экземплярах A (которые указывают на экземпляры B) теперь указать неправильные адреса - и тем самым привести к проблемам, которые приводят к сбою.

По поводу дрожания камеры: по-видимому, тряска камеры приводила к очень размытым изображениям с большим количеством элементов для сегментации, что увеличивало количество объектов (т. е. приводило к увеличению размера, необходимого для listOfB). Итак, тайна раскрыта! Большое спасибо! :-)


person kushy    schedule 05.02.2016    source источник
comment
Я думаю, что дизайн сломан. listofB будет увеличиваться и перераспределять свой внутренний массив данных, делая недействительными все адреса, хранящиеся в ptrs экземпляров A. Обычный алгоритм увеличит размер данных в 2 раза, что может объяснить, что у вас все хорошо на какое-то время, если не поступает слишком много данных. Кроме того, пока память старых данных все еще находится в адресном пространстве программы (особенно если она находится на той же странице памяти, например потому, что в нее помещаются и новые данные), программа может не вылететь при доступе к это и просто получить старые данные.   -  person Peter - Reinstate Monica    schedule 05.02.2016
comment
@PeterA.Schneider Это имело бы смысл! Таким образом, вектор указателей в экземплярах B - это не проблема, а только указатели в экземплярах A, я думаю (из-за изменения размера listOfB, который изменяет адреса памяти)? Если вы сделаете из этого ответ, я приму его.   -  person kushy    schedule 05.02.2016


Ответы (2)


Я думаю, что дизайн сломан. listofB будет расти (вы выполняете push_backs) и перераспределяет свой внутренний массив данных, делая недействительными все адреса, хранящиеся в ptrs экземпляров A. Обычный алгоритм увеличит размер данных в 2 раза, что может объяснить, что у вас все хорошо на какое-то время, если не поступает слишком много данных. Кроме того, пока память старых данных все еще находится в адресном пространстве программы (особенно если она находится на той же странице памяти, например потому, что в нее помещаются и новые данные), программа может не вылететь при доступе к это и просто получить старые данные.

Более конструктивно: ваше решение будет работать, если вы заранее знаете максимальное количество элементов, что может быть сложно (думаю, вы получите камеру 4k в следующем году ;-)). В этом случае вы можете, кстати, просто взять простой статический массив.

Возможно, вы могли бы также использовать std::map для хранения объектов A вместо простого векторного listofA. Каждому объекту A потребуется какой-то уникальный идентификатор (в простейшем случае статический счетчик в A), чтобы использовать его в качестве ключа на карте. Bs будет хранить ключи, а не адреса As.

person Peter - Reinstate Monica    schedule 05.02.2016
comment
На данный момент я знаю неплохой предел для максимального количества элементов, которые могут появиться, поэтому я думаю, что пока попробую использовать простой массив. Незнание точного размера заполненной части массива раздражает, но я думаю, что могу просто использовать для этого внешний счетчик. Но, как вы сказали, это всего лишь временное решение, так как лимит будет отличаться в зависимости от камеры или даже захваченного ландшафта. Может быть, я попытаюсь сделать простую формулу для предела, основанную на этих критериях. Я также изучу std::map, когда у меня будет время. Спасибо еще раз! :-) - person kushy; 05.02.2016

Предполагая, что вы не ошиблись при построении сети, все должно быть в порядке. Вам нужно будет опубликовать больше кода, чтобы оценить это. Также вы не можете использовать ни один из векторов после изменения одного из них, потому что, если они перераспределяют свои элементы, все указатели, указывающие на них, становятся недействительными. Но использование необработанных указателей на управляемые объекты — правильный способ построения сетей.

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

Однако похоже, что у вас аппаратная проблема.

person Galik    schedule 05.02.2016
comment
Сейчас я попробовал пару вещей, и то, что вы, ребята, сказали раньше, было правильным: я увеличил пространство, зарезервированное для listOfB (таким образом, гарантируя, что размер listOfB не будет изменен), и теперь все работает! Я также немного протестировал его и могу подтвердить, что каждый раз, когда код создавал listOfB с размером, превышающим зарезервированный, он давал сбой! Я предполагаю, что изменение размера заставило векторы указателей в этих объектах делать странные вещи. В любом случае, если бы вы могли опубликовать этот ответ еще раз, я приму его. В любом случае большое спасибо всем присутствующим! :-) - person kushy; 05.02.2016