Для чего используется Shared_ptr(shared_ptr<Y> const & r, T * p) Boost?

StackOverflow https://stackoverflow.com/questions/1403465

Вопрос

boost::shared_ptr имеет необычный конструктор

template<class Y> shared_ptr(shared_ptr<Y> const & r, T * p);

и я немного озадачен тем, для чего это может быть полезно.По сути, он разделяет собственность с r, но .get() вернется p. нет r.get()!

Это означает, что вы можете сделать что-то вроде этого:

int main() {
    boost::shared_ptr<int> x(new int);
    boost::shared_ptr<int> y(x, new int);

    std::cout << x.get() << std::endl;
    std::cout << y.get() << std::endl;

    std::cout << x.use_count() << std::endl;
    std::cout << y.use_count() << std::endl;
}

И вы получите это:

0x8c66008
0x8c66030
2
2

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

Итак int принадлежит x будет существовать до тех пор, пока x или y находится вокруг.И если я правильно понимаю документацию, второй int никогда не разрушается.Я подтвердил это с помощью следующей тестовой программы:

struct T {
    T() { std::cout << "T()" << std::endl; }
    ~T() { std::cout << "~T()" << std::endl; }
};

int main() {
    boost::shared_ptr<T> x(new T);
    boost::shared_ptr<T> y(x, new T);

    std::cout << x.get() << std::endl;
    std::cout << y.get() << std::endl;

    std::cout << x.use_count() << std::endl;
    std::cout << y.use_count() << std::endl;
}

Это выводит (как и ожидалось):

T()
T()
0x96c2008
0x96c2030
2
2
~T()

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

Это было полезно?

Решение

Это полезно, когда вы хотите предоставить общий доступ к члену класса, а экземпляр класса уже является shared_ptr, как показано ниже:

struct A
{
  int *B; // managed inside A
};

shared_ptr<A>   a( new A );
shared_ptr<int> b( a, a->B );

они разделяют счет использования и прочее. Это оптимизация для использования памяти.

Другие советы

Чтобы расширить Лейз и Петра ответы, это описание shared_ptr<> «алиасинг» взят из документа WG21, «Улучшение shared_ptr для C++0x, версия 2":

III.Поддержка псевдонимов

Продвинутые пользователи часто требуют возможности создания shared_ptrпример p Это разделяет право собственности с другим (мастер) shared_ptr q но указывает на объект, который не является основой *q. *p может быть членом или элементом *q, например.В этом разделе предлагается дополнительный конструктор, который можно использовать для этой цели.

Интересным побочным эффектом этого увеличения выразительной силы является то, что теперь *_pointer_cast Функции могут быть реализованы в коде пользователя.А make_shared Заводская функция, представленная позже в этом документе, также может быть реализована только с помощью публичного интерфейса shared_ptr через псевдоним конструктор.

Влияние:

Эта функция расширяет интерфейс shared_ptr обратно совместимым с способом, который увеличивает ее выразительную мощность и поэтому настоятельно рекомендуется добавлять в стандарт C ++ 0x.Он не вводит вопросы совместимости источника и бинарной совместимости.

Предлагаемый текст:

добавить в shared_ptrutil.smartptr.shared] Следующий конструктор:

template<class Y> shared_ptr( shared_ptr<Y> const & r, T * p );

Добавьте следующее в [util.smartptr.shared.const]:

template<class Y> shared_ptr( shared_ptr<Y> const & r, T * p );

Последствия: Конструирует shared_ptr экземпляр, который хранит p и разделяет собственность с r.

Постусловия: get() == p && use_count() == r.use_count().

Броски: ничего.

[Примечание: Чтобы избежать возможности свисающего указателя, пользователь этого конструктора должен обеспечить, чтобы p остается действительным, по крайней мере, до тех пор, пока группа владения r разрушен. --конец примечания.]

[Примечание: Этот конструктор позволяет создать пустой shared_ptrэкземпляр с сохраненным указателем, отличным от NULL. --конец примечания.]

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

class A {};
class B: public A {};

shared_ptr<A> a(new B);
shared_ptr<B> b(a, dynamic_cast<B*>(a.get()));

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

Я использовал конструктор псевдонимов shared_ptr в моей маленькой библиотеке:

http://code.google.com/p/infectorpp/ (только мой простой контейнер IoC)

Дело в том, что, так как мне нужно, чтобы shared_ptr известного типа был возвращен из полиморфного класса (который не знает тип). Я не смог неявно преобразовать shared_ptr в нужный мне тип.

В файле " InfectorHelpers.hpp. & Quot; (строка 72-99) вы можете видеть это в действии для типа IAnyShared.

Конструктор псевдонимов создает shared_ptr, который не удаляет указатели, на которые они фактически указывают, но они все же увеличивают счетчик ссылок по сравнению с исходным объектом, что может быть чрезвычайно полезным.

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

//my class
std::shared_ptr<T> ist;
int a; //dummy variable. I need its adress

virtual std::shared_ptr<int> getReferenceCounter(){
    return std::shared_ptr<int>(ist,&a); //not intended for dereferencing
}

virtual void* getPtr(); //return raw pointer to T

теперь у нас есть оба "счетчик ссылок" и указатель на значение T, достаточное количество данных для создания чего-либо с помощью конструктора псевдонимов

std::shared_ptr<T> aPtr( any->getReferenceCounter(), //share same ref counter 
               static_cast<T*>(any->getPtr()) ); //potentially unsafe cast!

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

Для " shared_ptr < B > b (a, dynamic_cast < B * > (a.get ())); "

Я думаю, что не рекомендуется использовать умный указатель.

Рекомендуемый способ выполнения этого преобразования типов должен быть следующим:

shared_ptr<B> b(a);

Поскольку в документе Boost упоминается, что:

  

shared_ptr < T > может быть неявно   преобразуется в shared_ptr < U > всякий раз, когда T *   может быть неявно преобразован в U *. В   в частности, shared_ptr < T >   неявно преобразуется в shared_ptr < T > Const ,   в shared_ptr < U > , где U является   доступная база Т, и к   <Код> shared_ptr & Lt; & пустоты GT;.

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

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top