Вызывается ли деструктор при удалении элемента из контейнера STL?

Скажем, у меня есть два контейнера, в которых хранятся указатели на одни и те же объекты:

std::list<Foo*> fooList;
std::vector<Foo*> fooVec;

Допустим, я удаляю объект из одного из этих контейнеров через один, если его методы:

std::vector<Foo*>::iterator itr = 
  std::find( fooVec.begin(), fooVec.end(), pToObj );
fooVec.erase( itr );

CppReference говорит, что это вызывает деструктор объекта. Означает ли это, что указатель на объект в fooList такое висячий указатель?

Я бы предпочел не использовать указатели с подсчетом ссылок. Как можно решить эту проблему?

5 ответов

Решение

Нет.

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

Однако опасно иметь указатели вещей в контейнерах. Рассматривать:

std::vector<int*> v;
v.push_back(new int());
v.push_back(new int());
v.push_back(new int());

Если вы никогда не пройдете через контейнер и не удалите каждый из них, вы просочились. Хуже того, это не исключение. Вы должны использовать указатель контейнера, который будет удалять вещи, на которые он указывает, когда они будут удалены. (И все стираются, когда контейнер разрушается.)

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

Я не думаю, что деструктор объекта будет вызван. Указатели в fooList должен по-прежнему указывать на действительные данные.

Если вы ссылаетесь на эту ссылку, речь идет о том, как звонки на erase сделает недействительными любые итераторы, которые вы могли бы указать на последующие местоположения в векторе. Но аннулирование итераторов отличается от вызова delete на одной из вещей в векторе.

В вашем случае объекты, хранящиеся в контейнерах, являются копией исходного указателя, а не исходного указателя. Так что для каждого Foo* что вы решили сохранить у вас будет 3 указателя (оригинал, один в fooList и один в fooVec), все указывают на одно и то же место в памяти. Поэтому, когда вы звоните erase удаление будет вызываться на самом указателе, а не на том, на что он указывает, а удаление на указателях - нет операции (у них нет деструкторов, как сказал GMan).

Оба контейнера содержат ссылки на объекты (Foo *) - поэтому, если вызывается деструктор, это деструктор объекта-указателя (который, вероятно, ничего не делает), а не сам объект Foo. Исходный объект (класса Foo) не уничтожен, и поэтому нет висящих ссылок.

Когда у вас есть необработанный указатель на объект, деструктор не вызывается, пока вы его не удалите.

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

Другие вопросы по тегам