Для чего используется Shared_ptr(shared_ptr<Y> const & r, T * p) Boost?
-
05-07-2019 - |
Вопрос
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_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
экземпляр с сохраненным указателем, отличным от 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, и оба эти метода были бы намного безопаснее, чем способ приведения необработанного указателя вручную.