Как безопасно удалить несколько указателей - PullRequest
5 голосов
/ 14 октября 2010

У меня есть код, который использует множество указателей, указывающих на один и тот же адрес.Дан эквивалентный простой пример:

int *p =  new int(1);
int *q = p;
int *r = q;

delete r; r = NULL; // ok
// delete q; q = NULL; // NOT ok
// delete p; p = NULL; // NOT ok

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

Ответы [ 8 ]

28 голосов
/ 14 октября 2010

Ваш инструмент shared_ptr из библиотеки boost. Взгляните на документацию: http://www.boost.org/doc/libs/1_44_0/libs/smart_ptr/shared_ptr.htm

Пример:

void func() {
  boost::shared_ptr<int> p(new int(10));
  boost::shared_ptr<int> q(p);
  boost::shared_ptr<int> r(q);

  // will be destructed correctly when they go out of scope.
}
18 голосов
/ 14 октября 2010

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

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

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

10 голосов
/ 14 октября 2010

«Современный» ответ - использовать умный указатель и не удалять вручную.

boost::shared_ptr<int> p(new int(1));
boost::shared_ptr<int> q = p;
boost::shared_ptr<int> r = q;

Конец истории!

3 голосов
/ 14 октября 2010

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

Как только вы узнаете, кому принадлежит память, вернитесь к кодуи реализовать это.Если объект является единственным ответственным за другой объект, он должен удерживать его с помощью интеллектуального указателя одного владельца (std::auto_ptr / unique_ptr) или даже необработанного указателя (попытайтесь избежать этого, поскольку он является общим источникомошибки) и управлять памятью вручную.Затем передайте ссылки или указатели на другие объекты.Когда право собственности передается, используйте умные указатели, чтобы передать объект новому владельцу.Если право собственности действительно является общим (нет явного владельца выделенного объекта), тогда вы можете использовать shared_ptr и позволить интеллектуальному указателю иметь дело с управлением памятью).

1 голос
/ 14 октября 2010

Почему вы пытаетесь произвольно удалить указатели? Каждый динамически размещенный объект размещается в одном месте, владельцем одним . И должно быть, что ответственность за повторное удаление объекта лежит на одном владельце.

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

А иногда вы просто хотите забыть о владении и использовать совместное владение владение: каждый, кто использует объект, делится с владельцами, и пока существует хотя бы один пользователь, объект не следует удалять. 1013 *

Тогда вы используете shared_ptr.

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

0 голосов
/ 24 июня 2012

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

void safeDelete(void **ptr)
{
  if(*ptr != NULL)
  {
     delete ptr;
     *ptr = NULL;
  }
}

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

0 голосов
/ 14 октября 2010

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

0 голосов
/ 14 октября 2010

В некоторых очень редких случаях вы не можете использовать умный указатель (возможно, имеющий дело со старым кодом), но также не можете использовать простую схему "владения".

Представьте, что у вас есть std::vector<whatever*>, и некоторые из whatever* указателей указывают на один и тот же объект. Безопасная очистка подразумевает, что вы не удаляете одно и то же дважды, поэтому создайте std::set<whatever*> по ходу работы и удаляйте только те указатели, которых еще нет в наборе. После удаления всех указанных объектов можно безопасно удалить оба контейнера.

Возвращаемое значение из insert может использоваться для определения, был ли вставленный элемент новым или нет. Я не проверял следующее (или некоторое время использовал std :: set), но думаю, что это правильно ...

if (myset.insert (pointervalue).second)
{
  //  Value was successfully inserted as a new item
  delete pointervalue;
}

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

...