Отделить указатель от shared_ptr?
Возможный дубликат:
Как освободить указатель от boost:: shared_ptr?
Функция моего интерфейса возвращает указатель на объект. Пользователь должен взять на себя ответственность за этот объект. Я не хочу возвращать Boost.shared_ptr, потому что я не хочу заставлять клиентов использовать boost. Тем не менее, внутренне я хотел бы сохранить указатель в shared_ptr, чтобы предотвратить утечку памяти в случае исключений и т.д. Кажется, что нет способа отделить указатель от общего указателя. Любые идеи здесь?
Ответы
Ответ 1
То, что вы ищете, - это функция release
; shared_ptr
не имеет функции деблокирования. В руководстве Boost:
Q. Почему shared_ptr не предоставляет функцию release()?
а. shared_ptr не может отдать право собственности, если оно не уникально(), потому что другая копия все равно уничтожит объект.
Рассмотрим:
shared_ptr<int> a(new int);
shared_ptr<int> b(a); // a.use_count() == b.use_count() == 2
int * p = a.release();
// Who owns p now? b will still call delete on it in its destructor.
Кроме того, указатель, возвращаемый release(), будет трудно освободить навсегда, поскольку исходный shared_ptr мог быть создан с пользовательским удалением.
Два варианта, которые вы могли бы рассмотреть:
- Вы можете использовать
std::tr1::shared_ptr
, что потребует от ваших пользователей использования реализации библиотеки С++, поддерживающей TR1, или для использования Boost; по крайней мере, это даст им выбор между ними.
- Вы можете реализовать свой собственный
boost::shared_ptr
-подобный общий указатель и использовать его на внешних интерфейсах.
Вы также можете посмотреть обсуждение этого вопроса о с помощью boost:: shared_ptr в общедоступном интерфейсе библиотеки.
Ответ 2
всегда есть способ: -)
Существует действительно причина, по которой они не предоставляют метод release(), но создать его невозможно. Сделайте свой собственный делетер. Что-то в строке (на самом деле не скомпилировал код, но это общее понятие):
template <typename T>
class release_deleter{
public:
release_deleter() : released_(new some_atomic_bool(false)){}
void release() {released_->set(true);}
void operator()(T* ptr){if(!released_->get()) delete ptr;}
private:
shared_ptr<some_atomic_bool> released_;
}
..
shared_ptr<some_type> ptr(new some_type, release_deleter<some_type>());
..
release_deleter<some_type>* deleter = get_deleter<release_deleter<some_type>>(ptr);
deleter->release();
some_type* released_ptr = ptr.get();
Ответ 3
Пользователь должен взять на себя ответственность за этот объект. Я не хочу возвращать Boost.shared_ptr,
shared_ptr
выражает совместное владение, и вы хотите, чтобы ваш интерфейс выражал передачу права собственности. std::auto_ptr
будет здесь более применимым.
Внутри, однако, я хотел бы сохранить указатель в shared_ptr, чтобы предотвратить утечку памяти в случае исключений
Опять же, shared_ptr
может быть не лучшим инструментом для этой работы. Чтобы предотвратить утечку в случае исключений, лучше было бы использовать scoped_ptr
или auto_ptr
.
Ответ 4
Используйте shared_ptr
для scoped_ptr
для ресурса (shared_ptr<scoped_ptr<Resource>>
). Таким образом вы получите счетчик ссылок shared_ptr
, который автоматически уничтожит ресурс тогда и только тогда, когда он все еще привязан к scoped_ptr
. Но вы можете отсоединить scoped_ptr
, когда будете готовы отменить право собственности.
Ответ 5
Как Джеймс хорошо справился, вы не можете отсоединить общий указатель.
Вам нужны несколько владельцев внутри страны, или вы передаете право собственности от своего класса клиенту? В этом случае a std::auto_ptr
может соответствовать счету.
Если вас беспокоит удивительная семантика std::auto_ptr
, вы можете держать ее внутри boost::scoped_ptr
и отсоединять ее в том месте, где вы ее передаете, - оставляя ее клиенту для его ручного удаления или сохранения это в их собственном умном указателе.
Если у вас есть несколько владельцев на вашей стороне, вы можете использовать интрузивный счет. Внутри вы могли бы использовать boost::intrusive__ptr
, но отбросьте необработанный указатель на интерфейс. Клиент может либо вручную работать с подсчетом ссылок, либо хранить его в boost::intrusive_ptr
сами (но вы не заставляете их зависеть от него)