Указатели и коллекция указателей в C ++. Как правильно удалить - PullRequest
3 голосов
/ 25 мая 2010

Это новый вопрос, но у меня всегда есть сомнения относительно указателей в C ++. Это ситуация.

У меня есть класс A, который как коллекция (фактически вектор) указателей класса B. В этом же классе A есть еще одна коллекция указателей на класс C. Наконец, объекты класса B также имеют коллекцию указателей на класс C, которые указывают на те же экземпляры, на которые указывает класс A.

У меня вопрос: если я удалю член указателя типа класса C в классе B, что произойдет с указателем в классе A, который указывает на удаленный экземпляр класса C? Как эту ситуацию нужно лечить?

Заранее большое спасибо!

Julen.

Ответы [ 7 ]

6 голосов
/ 25 мая 2010

Мое понимание:

A
 - vector<B*>
 - vector<C*>

B
 - vector<C*>

Изнутри B вы удаляете все C *.


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

Любой указатель на удаленный адрес все еще содержит тот же адрес, он просто вызывает неопределенное поведение, если вы его используете.

Поэтому не следует повторно удалять B * из A, который вы уже удалили, и не использовать их после того, как они будут удалены.


Возможно, вы захотите пересмотреть свою иерархию / дизайн.

Или рассмотрите возможность использования boost :: shared_ptr , если вам нужно хранить вещи таким способом. Фактически каждый раз, когда вы храните коллекцию указателей, вы, вероятно, должны использовать boost :: shared_ptr. Если вы используете boost :: shared_ptr, вы не удаляете его, и вам не нужно беспокоиться об аннулировании других указателей.

5 голосов
/ 25 мая 2010

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

0 голосов
/ 25 мая 2010

Я бы сказал, что вам не хватает класса D.

D - это класс, которому принадлежат и A, B, C. Если объекты C создаются динамически, A должен создавать и хранить их, тогда как A и B могут использовать неуправляемые указатели.

Для реализации D boost :: ptr_vector может быть хорошим контейнером для объектов C, если они должны быть в форме указателя (полиморфные объекты) или простым вектором для старых добрых объектов.

0 голосов
/ 25 мая 2010

Если вы хотите использовать указатель и динамическое распределение, вам нужно определить, кому что принадлежит.

В вашем случае удаление C s из B s опасно, потому что после уничтожения B s они взяли с собой C s, хотя A все еще ссылка на них.

В вашей ситуации я бы лучше сделал следующее:

  • уничтожение C ничего не делает
  • уничтожение Б ничего не делает
  • уничтожение А уничтожает его коллекцию В и С

Это означает, что A является владельцем памяти и управляет жизненным циклом объектов, тогда как B s имеет ссылки только на C s.

Кроме того, я бы посоветовал использовать RAII (Инициализация ресурсов является инициализацией), чтобы очистка выполнялась автоматически для вас:

class A
{
public:
private:
  std::vector< std::unique_ptr<B> > bs;
  std::vector< std::unique_ptr<C> > cs;
};

// B and C are unchanged

Использование unique_ptr имеет 3 преимущества:

  • четко указывает, кто является владельцем памяти (в данном случае экземпляр A)
  • предотвращает непреднамеренное копирование экземпляра A, что может привести к хаосу в вашем дизайне
  • это упрощает обработку памяти, это автоматически, не беспокойтесь об этом
0 голосов
/ 25 мая 2010

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

Несколько решений:

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

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

0 голосов
/ 25 мая 2010

Если вы удаляете экземпляры для класса C из класса B, то указатели в классе A по-прежнему указывают на эти адреса ... что приводит к неопределенному поведению (скорее всего, ошибка сегмента). Вы должны убедиться, что если вы удалите экземпляр в одном месте, все остальные ссылки на этот экземпляр также будут обновлены.

В вашем случае вы захотите очистить коллекцию А до класса С.

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

0 голосов
/ 25 мая 2010

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...