Downcasting shared_ptr <Base> to shared_ptr <Производный>?
Обновление: shared_ptr в этом примере похоже на файл в Boost, но он не поддерживает shared_polymorphic_downcast (или dynamic_pointer_cast или static_pointer_cast, если на то пошло)!
Я пытаюсь инициализировать общий указатель на производный класс, не теряя счетчик ссылок:
struct Base { };
struct Derived : public Base { };
shared_ptr<Base> base(new Base());
shared_ptr<Derived> derived;
// error: invalid conversion from 'Base* const' to 'Derived*'
derived = base;
До сих пор так хорошо. Я не ожидал, что С++ неявно преобразует Base * в Derived *. Тем не менее, я хочу функциональность, выраженную кодом (то есть, поддерживая подсчет ссылок при подавлении базового указателя). Моя первая мысль заключалась в том, чтобы предоставить оператору трансляции в базе так, чтобы имело место неявное преобразование в Derived (для педантов: я бы проверял, что приведение вниз действительно, не волнуйтесь):
struct Base {
operator Derived* ();
}
// ...
Base::operator Derived* () {
return down_cast<Derived*>(this);
}
Ну, это не помогло. Кажется, компилятор полностью проигнорировал мой оператор-оператор. Любые идеи, как я мог бы сделать назначение shared_ptr работать? За дополнительные баллы: какой тип Base* const
? const Base*
Я понимаю, но Base* const
? Что означает const
в этом случае?
Ответы
Ответ 1
Вы можете использовать dynamic_pointer_cast
. Он поддерживается std::shared_ptr
.
std::shared_ptr<Base> base (new Derived());
std::shared_ptr<Derived> derived =
std::dynamic_pointer_cast<Derived> (base);
Кроме того, я не рекомендую использовать оператор-литье в базовом классе. Неявное литье, подобное этому, может стать источником ошибок и ошибок.
-Update: Если тип не является полиморфным, может использоваться std::static_pointer_cast
.
Ответ 2
Я предполагаю, что вы используете boost::shared_ptr
... Я думаю, вы хотите dynamic_pointer_cast
или shared_polymorphic_downcast
.
Однако для этого требуются полиморфные типы.
какой тип Base* const
? const Base*
Я понимаю, но Base* const
? Что означает const
в этом случае?
-
const Base *
является изменяемым указателем на константу Base
.
-
Base const *
является изменяемым указателем на константу Base
.
-
Base * const
- постоянный указатель на изменяемый Base
.
-
Base const * const
- постоянный указатель на константу Base
.
Здесь минимальный пример:
struct Base { virtual ~Base() { } }; // dynamic casts require polymorphic types
struct Derived : public Base { };
boost::shared_ptr<Base> base(new Base());
boost::shared_ptr<Derived> derived;
derived = boost::static_pointer_cast<Derived>(base);
derived = boost::dynamic_pointer_cast<Derived>(base);
derived = boost::shared_polymorphic_downcast<Derived>(base);
Я не уверен, было ли умышленно, чтобы ваш пример создавал экземпляр базового типа и отбрасывал его, но он служит для иллюстрации различия.
static_pointer_cast
будет "просто делать это". Это приведет к поведению undefined (a Derived*
, указывающему на выделенную память и инициализированную с помощью Base
) и, скорее всего, приведет к сбою или, что еще хуже. Счетчик ссылок на Base
будет увеличен.
dynamic_pointer_cast
приведет к нулевому указателю. Счетчик ссылок на Base
не изменится.
shared_polymorphic_downcast
будет иметь тот же результат, что и статический приведение, но вызовет утверждение, скорее похожее на успех, и приведет к поведению undefined. Счетчик ссылок на Base
будет увеличен.
См. (мертвая ссылка):
Иногда трудно решить, использовать ли static_cast
или dynamic_cast
, и вы хотите, чтобы у вас было немного двух миров. Хорошо известно, что dynamic_cast имеет служебные данные во время выполнения, но он безопаснее, тогда как static_cast вообще не имеет накладных расходов, но он может терпеть неудачу. Как хорошо было бы, если бы вы могли использовать shared_dynamic_cast
в отладочных сборках и shared_static_cast
в сборках релизов. Ну, такая вещь уже доступна и называется shared_polymorphic_downcast
.
Ответ 3
Если кто-то получает здесь с boost:: shared_ptr...
Вот как вы можете сбрасывать производные Boost shared_ptr. Предположим, что Derived наследует базу.
boost::shared_ptr<Base> bS;
bS.reset(new Derived());
boost::shared_ptr<Derived> dS = boost::dynamic_pointer_cast<Derived,Base>(bS);
std::cout << "DerivedSPtr is: " << std::boolalpha << (dS.get() != 0) << std::endl;
Убедитесь, что класс 'Base'/struct имеет хотя бы одну виртуальную функцию. Также работает виртуальный деструктор.