конвертировать shared_ptr в auto_ptr? - PullRequest
6 голосов
/ 23 января 2011

Мне нужно получить 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 ]

9 голосов
/ 23 января 2011

По замыслу это невозможно, поскольку объект может использоваться совместно с другим указателем общего доступа, и, таким образом, «извлечение» его в auto_ptr может привести к удалению ссылочного объекта.

По той же причине, что shared_ptr не имеет функции-члена «release»как auto_ptr.

Редактировать:

Даже если shared_ptr имел какой-то метод «release» или позволял удалить свою ссылку, не разрушая объект, он не будет работать в следующем случае (потокиА, Б):

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!
4 голосов
/ 23 января 2011

Общий указатель может быть общим для многих, но вы не можете просто так его отнять. Это разработано Артём и peoro .

Одним из подходов является создание временного 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
}
3 голосов
/ 23 января 2011

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

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

2 голосов
/ 23 января 2011

Если вы хотите передать владение из shared_ptr в auto_ptr, это возможно только тогда, когда

  • Счетчик ссылок shared_ptr равен 1,и
  • shared_ptr изначально был создан с пользовательской функцией удаления , а
  • вы знаете тип этой функции удаления.

Учитывая это, вот как:

#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;
}

Примечание: этот метод не является поточно-ориентированным.

Приветствия и hth.,

1 голос
/ 23 января 2011

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

Но вы можете сделать это, но обязательно позвоните release, прежде чем выпадете из области видимости.

void foo(shared_ptr<Y> s)
{
    auto_ptr<Y> a(s.get());

    // use a

    a.release();
}

РЕДАКТИРОВАТЬ: вышеупомянутое решение не является безопасным для исключения. Следующее должно работать, комбинируя класс 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);
}
...