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 имеет хотя бы одну виртуальную функцию. Также работает виртуальный деструктор.