Как выполнить динамический_каст с уникальным_ptr?
У меня есть иерархия классов следующим образом:
class BaseSession : public boost::enable_shared_from_this<BaseSession>
class DerivedSessionA : public BaseSession
class DerivedSessionB : public BaseSession
Внутри производных функций класса я регулярно вызываю такие функции:
Func(boost::dynamic_pointer_cast<DerivedSessionA>(shared_from_this()));
Поскольку я работал с shared_ptr
для управления сеансами, это работало нормально. Недавно я обнаружил, что мое использование shared_ptr
для этого случая не является оптимальным. Это потому, что эти сеансы - это одноэлементные объекты, которые поддерживают один сокет на каждого клиента. Если сокет снова подключен, копии сеанса стали зомби.
В качестве обходного пути я начал передавать shared_ptr
по ссылке, а не по копиям. Это решило проблему зомби.
В идеале я чувствовал, что должен использовать unique_ptr
для хранения сеанса, а затем передавать ссылки на другие функции. Это открыло целую банку червей.
Как сделать класс базового класса unique_ptr
объектом производного класса unique_ptr
? Что такое unique_ptr
версия следующей строки?
Func(boost::dynamic_pointer_cast<DerivedSessionA>(shared_from_this()));
Мне просто нужна одна копия объекта сеанса, все остальное должно быть ссылкой.
Ответы
Ответ 1
Если вы не хотите передать право собственности на свой std::unique_ptr<T>
, ваша функция должна взять указатель или ссылку на T
.
Таким образом, подпись Func
должна быть чем-то вроде Func(DerivedSessionA*)
а затем ваш вызов может выглядеть так:
std::unique_ptr<BaseSession> ptr; // Initialize it with correct value
Func(dynamic_cast<DerivedSessionA*>(ptr.get()));
Или, как вы, кажется, вызываете его непосредственно из метода в BaseSession
:
Func(dynamic_cast<DerivedSessionA*>(this));
Ответ 2
Update
Вопрос выяснен:
Извините, я не был ясен. Я хочу, чтобы собственность оставалась с оригинальным владельцем, вызываемая функция должна только ссылаться на нее, а не на владение. Не ищете двух умных указателей для одного и того же объекта.
В этом случае решение просто:
dynamic_cast<B&>(*my_unique_ptr)
Готово. Он бросает, если приведение не выполняется.
Литье shared_ptr
Для shared_ptr
существует std::dynamic_pointer_cast<>
(http://en.cppreference.com/w/cpp/memory/shared_ptr/pointer_cast)
Литье unique_ptr
Самый простой способ:
#include <memory>
struct A { virtual ~A() = default; };
struct B : A { };
int main()
{
std::unique_ptr<A> pa(new B);
std::unique_ptr<B> pb(dynamic_cast<B*>(pa.release())); // DO NOT DO THIS
}
Как справедливо отмечает комментатор, это может привести к утечке объекта, если преобразование завершилось неудачно. Это не очень полезно.
Причина, по которой dynamic_unique_ptr_cast<>
не существует, может заключаться в том, что тип unique_ptr
не удаляет дебетер. Было бы сложно/невозможно выбрать подходящее удаление для целевого типа указателя.
Однако для простых случаев вы можете использовать что-то вроде этого:
template <typename To, typename From, typename Deleter>
std::unique_ptr<To, Deleter> dynamic_unique_cast(std::unique_ptr<From, Deleter>&& p) {
if (To* cast = dynamic_cast<To*>(p.get()))
{
std::unique_ptr<To, Deleter> result(cast, std::move(p.get_deleter()));
p.release();
return result;
}
return std::unique_ptr<To, Deleter>(nullptr); // or throw std::bad_cast() if you prefer
}
auto pb = dynamic_unique_cast<B>(std::move(pa));
Ответ 3
Просто введите сохраненный указатель с помощью метода std::unique_ptr<>::get()
:
Func(dynamic_cast<DerivedSessionA*>(shared_from_this().get()))
что если shared_from_this()
имеет этот прототип:
std::unique_ptr<BaseSession>& shared_from_this();