Для чего используется Boost's shared_ptr (shared_ptr <Y>const & r, T * p)? - PullRequest
26 голосов
/ 10 сентября 2009

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()

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

Ответы [ 6 ]

29 голосов
/ 10 сентября 2009

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

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

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

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

8 голосов
/ 10 сентября 2009

Чтобы развернуть ответы leiz и piotr , это описание shared_ptr<> 'aliasing' взято из статьи WG21, "Improving shared_ptr для C ++" 0x, редакция 2 ":

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

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

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

Impact:

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

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

Добавить к shared_ptr [util.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 экземпляр с ненулевым хранимым указателем. - конечная нота.]

4 голосов
/ 11 августа 2010

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

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

shared_ptr<A> a(new B);
shared_ptr<B> b(a, dynamic_cast<B*>(a.get()));
2 голосов
/ 10 сентября 2009

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

0 голосов
/ 13 июля 2013

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

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

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

В файле " InfectorHelpers.hpp " (строка 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!

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

0 голосов
/ 14 января 2011

Для "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<void>.

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

...