Даункинг с shared_ptr <Base>до shared_ptr <Derived>? - PullRequest
83 голосов
/ 31 августа 2009

Обновление: shared_ptr в этом примере похож на тот, что в Boost, но не поддерживает shared_polymorphic_downcast (или динамически_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;  

Пока все хорошо. Я не ожидал, что C ++ неявно преобразует Base * в Derived *. Однако я хочу, чтобы функциональность выражалась кодом (то есть поддержание счетчика ссылок при понижении базового указателя). Моей первой мыслью было создание оператора приведения в Base, чтобы неявное преобразование в Derived могло иметь место (для педантов: я бы проверил, что приведение к понижению допустимо, не волнуйтесь):

struct Base {
  operator Derived* ();
}
// ...
Base::operator Derived* () {
  return down_cast<Derived*>(this);
}

Ну, это не помогло. Кажется, компилятор полностью проигнорировал мой оператор typecast. Любые идеи, как я мог бы сделать назначение shared_ptr работать? Для дополнительных очков: что это за тип Base* const? const Base* Я понимаю, но Base* const? Что означает const в этом случае?

Ответы [ 3 ]

78 голосов
/ 27 января 2013

Вы можете использовать dynamic_pointer_cast. Поддерживается std::shared_ptr.

std::shared_ptr<Base> base (new Derived());
std::shared_ptr<Derived> derived =
               std::dynamic_pointer_cast<Derived> (base);

Кроме того, я не рекомендую использовать оператор приведения в базовом классе. Подобное неявное приведение может стать источником ошибок и ошибок.

-Обновление: Если тип не полиморфный, можно использовать std::static_pointer_cast.

46 голосов
/ 31 августа 2009

Я полагаю, вы используете 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 "просто сделает это". Это приведет к неопределенному поведению (Derived*, указывающему на память, выделенную и инициализированную Base) и, скорее всего, приведет к сбою или, что еще хуже. Счетчик ссылок на base будет увеличен.

dynamic_pointer_cast приведет к нулевому указателю. Счетчик ссылок на base не изменится.

shared_polymorphic_downcast будет иметь тот же результат, что и статическое приведение, но вызовет утверждение, скорее всего, будет успешным и приведет к неопределенному поведению. Счетчик ссылок на base будет увеличен.

См. (неработающая ссылка) :

Иногда бывает немного трудно решить, использовать ли static_cast или dynamic_cast, и вы хотели бы иметь немного обоих миров. Хорошо известно, что dynamic_cast имеет накладные расходы времени выполнения, но он безопаснее, тогда как static_cast вообще не имеет никаких издержек, но может молча завершиться сбоем. Как было бы хорошо, если бы вы могли использовать shared_dynamic_cast в отладочных сборках и shared_static_cast в сборках релизов. Ну, такая вещь уже доступна и называется shared_polymorphic_downcast.

4 голосов
/ 17 апреля 2015

Если кто-то попадет сюда с boost :: shared_ptr ...

Вот как вы можете понизить до производного Boost shared_ptr. Предполагая, что Derived наследует от Base.

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;

Убедитесь, что в базовом классе / структуре есть хотя бы одна виртуальная функция. Виртуальный деструктор также работает.

...