Подтвердить что ты не робот

Что такое boost shared_ptr (shared_ptr <Y> const & r, T * p), используемый для?

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

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

4b9b3361

Ответ 1

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

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

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

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

Ответ 2

Чтобы расширить leiz и ответы piotr, это описание shared_ptr<> 'aliasing' от документ WG21, "Улучшение shared_ptr для С++ 0x, версия 2" :

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

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

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

Влияние:

Эта функция расширяет интерфейс shared_ptr в обратной совместимости способ, который увеличивает его выразительность и поэтому сильно рекомендуется добавлять в С++ 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 не будет уничтожена. --end note.]

     

[Примечание. Этот конструктор позволяет создать пустой shared_ptr  экземпляр с указателем, не содержащим NULL. --end note.]

Ответ 3

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

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

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

Ответ 4

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

Ответ 5

Для "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 - доступная база T и shared_ptr<void>.

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

Ответ 6

Я использовал конструктор aliasing 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!

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