Что такое 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()
Итак... Какова польза от этой необычной конструкции, которая разделяет владение одним указателем, но действует как другой указатель (который он не имеет) при использовании.
Ответы
Ответ 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!
Я не претендую на то, что изобрел это использование для конструктора псевдонимов, но я никогда не видел, чтобы кто-то другой делал то же самое. Если вы догадываетесь, работает ли этот грязный код, да.