преобразовать общий ptr в автоматический ptr?
Мне нужно получить auto_ptr из shared_ptr в моем коде. Я могу сделать обратную операцию-преобразовать auto_ptr в shared_ptr, так как shared_ptr имеет такой конструктор:
template<class Y> explicit shared_ptr(std::auto_ptr<Y> & r);
Можно ли преобразовать shared_ptr в auto_ptr? Или это невозможно по замыслу?
5 ответов:
Общий указатель может быть общим для многих вещей, вы не можете просто взять его у всех так или иначе. Это разработано Артемом и пеоро.
Один из подходов состоит в том, чтобы сделать временныйauto_ptr
и освободить его от обработки указателя в конце области видимости. dalle описывает первый подход, но он страдает от отсутствия безопасности исключений (может случайно удалить), и он не может защитить вас от случайной передачи его функции, которая собирается передать владение (где удаление выпадает из наших рук).Мы можем сделать нашу собственную обертку, чтобы избежать этого, хотя:
template <typename T> class auto_ptr_facade { public: auto_ptr_facade(shared_ptr<T> ptr) : mPtr(ptr), mAuto(ptr.get()) {} ~auto_ptr_facade() { // doesn't actually have ownership mAuto.release(); } // only expose as const, cannot be transferred const auto_ptr<T>& get() const { return mAuto; } operator const auto_ptr<T>&() const { return get(); } private: auto_ptr_facade(const auto_ptr_facade&); auto_ptr_facade& operator=(const auto_ptr_facade&); shared_ptr<T> mPtr; auto_ptr<T> mAuto; };
Теперь вы можете рассматривать
shared_ptr
какconst auto_ptr
, в области:template <typename T> void foo(shared_ptr<T> ptr) { auto_ptr_facade<T> a(ptr); // use a }
Это невозможно по замыслу, так как объект может быть совместно использован с другим общим указателем и, таким образом," выборка " его в auto_ptr может привести к удалению объекта, на который ссылается ссылка.
По той же причине shared_ptr не имеет функции-члена" release", что и auto_ptr.
Редактировать:
Даже если shared_ptr имел какой-то метод" release " или позволял удалить его ссылку без разрушения объекта он не будет работать в следующем случае (потоки A, B):
A: { A: int count = sp.use_count(); Context Switch B: shared_ptr<bar> my_sp = weak_sp.lock(); B: // now use_count = 2 but A thinks it is 1 Context Switch A: auto_ptr<bar> ap; A: if(count == 1) A: ap.reset(sp.release()); A: // actutally there is no sp.release but what if A: ap->foo(); A: } // delete the object pointer by ap as it goes out of scope Context Switch B: my_sp->foo(); // Ooops - object is deleted!
Обычно это плохая идея, так как и
auto_ptr
, иshared_ptr
берут на себя владение вашим указателем (они будут заботиться о том, чтобы уничтожить его и прочее, в соответствии с различными политиками).Наличие двух различных объектов, имеющих право собственности на один и тот же указатель, скорее всего, приведет к ошибкам во время выполнения, если вы не делаете это для некоторых действительно хороших (и странных!) причины.
Предполагая, что вы хотите передать владение от
shared_ptr
кauto_ptr
, это возможно только тогда, когда
- отсчет ссылок
shared_ptr
равен 1, аshared_ptr
был первоначально создан с помощью пользовательской функции deleter и- вы знаете тип этой функции делетера.
Учитывая это, вот как:
Примечание: этот метод не является потокобезопасным.#include <iostream> #include <boost/shared_ptr.hpp> // boost::shared_ptr #include <memory> // std::auto_ptr typedef boost::shared_ptr<int> IntSharedPtr; typedef std::auto_ptr<int> IntAutoPtr; template< class Type > void myCustomDeleter( Type* p ) { delete p; } IntSharedPtr newSharedInt() { return IntSharedPtr( new int( 42 ), &myCustomDeleter<int> ); } IntAutoPtr makeAutoFrom( IntSharedPtr& sp ) { struct Dummy { static void deleter( int* ) {} }; typedef void (*DeleterFunc)( int* ); if( sp.use_count() > 1 ) { return IntAutoPtr( 0 ); } DeleterFunc* d = boost::get_deleter<DeleterFunc>( sp ); if( d == 0 ) { return IntAutoPtr( 0 ); } int* const p = sp.get(); *d = &Dummy::deleter; sp.reset(); return IntAutoPtr( p ); } template< class T > T& refTo( T const& r ) { return const_cast< T& >( r ); } int main() { IntAutoPtr p( makeAutoFrom( refTo( newSharedInt() ) ) ); std::cout << (p.get() == 0? "Failed" : "Worked" ) << std::endl; }
Ура и ХТ.,
Вы не должны этого делать, так как
auto_ptr
становится владельцем указателя.Но вы можете сделать это, но убедитесь, что вы вызываете
release
, прежде чем вы выпадете из области видимости.void foo(shared_ptr<Y> s) { auto_ptr<Y> a(s.get()); // use a a.release(); }
EDIT: приведенное выше решение не является безопасным для исключений. Должно работать следующее, объединяя класс guard с гарантией того, что класс
const auto_ptr
не может быть скопирован:void bar(const auto_ptr<Y>& p); struct as_const_auto_ptr { explicit as_const_auto_ptr(const shared_ptr<Y>& p) : p(p), a(p.get()) {} ~as_const_auto_ptr() {a.release();} operator const auto_ptr<Y>&() {return a;} const shared_ptr<Y> p; auto_ptr<Y> a; }; void foo(shared_ptr<Y> s) { as_const_auto_ptr a(s); // use a. bar(a); }