Обновление: 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;
Все идет нормально. Я не ожидал, что C ++ неявно преобразует Base * в Derived *. Однако мне нужны функциональные возможности, выраженные в коде (то есть поддержание счетчика ссылок при понижении базового указателя). Моя первая мысль заключалась в том, чтобы предоставить оператор приведения в Base, чтобы могло происходить неявное преобразование в Derived (для педантов: я бы проверил, что приведение вниз действительно, не волнуйтесь):
struct Base {
operator Derived* ();
}
// ...
Base::operator Derived* () {
return down_cast<Derived*>(this);
}
Что ж, это не помогло. Кажется, компилятор полностью проигнорировал мой оператор приведения типов. Есть идеи, как заставить работать задание shared_ptr? За дополнительные баллы: что это за тип Base* const
? const Base*
Я понимаю, но Base* const
? Что имеется в const
виду в этом случае?
Ответы:
Вы можете использовать
dynamic_pointer_cast
. Поддерживаетсяstd::shared_ptr
.Документация: https://en.cppreference.com/w/cpp/memory/shared_ptr/pointer_cast
Также я не рекомендую использовать оператор приведения в базовом классе. Подобное неявное приведение типов может стать источником ошибок и ошибок.
-Update:
std::static_pointer_cast
может использоваться, если тип не полиморфен .источник
std::shared_ptr
. Но из комментариев к первому ответу я сделал вывод, что он не использует ускорение, поэтому он может использоватьstd::shared_ptr
.Я полагаю, вы используете
boost::shared_ptr
... Я думаю, вы хотитеdynamic_pointer_cast
илиshared_polymorphic_downcast
.Однако для этого требуются полиморфные типы.
const Base *
- изменяемый указатель на константуBase
.Base const *
- изменяемый указатель на константуBase
.Base * const
постоянный указатель на изменяемыйBase
.Base const * const
постоянный указатель на константуBase
.Вот минимальный пример:
Я не уверен, было ли намеренно, что ваш пример создает экземпляр базового типа и приводит его, но он служит для красивой иллюстрации разницы.
static_pointer_cast
Будет «просто сделать это». Это приведет к неопределенному поведению (Derived*
указание на память, выделенную и инициализированнуюBase
) и, вероятно, вызовет сбой или что-то еще хуже. Счетчик ссылокbase
будет увеличиваться.Результатом
dynamic_pointer_cast
будет пустой указатель. Количество ссылокbase
останется неизменным.shared_polymorphic_downcast
Будет иметь тот же результат , как статический бросок, но вызовет утверждение, а не кажущийся успех и приводит к неопределенному поведению. Счетчик ссылокbase
будет увеличиваться.См. (Мертвая ссылка) :
источник
shared_ptr
конструкторов (взявstatic_cast_tag
иdynamic_cast_tag
), вы мало что можете сделать. Все, что вы делаете за пределамиshared_ptr
, не сможет управлять реф-счетом. - В «идеальном» объектно-ориентированном дизайне вы всегда можете использовать базовый тип, и вам никогда не нужно знать и заботиться о том, что такое производный тип, потому что все его функциональные возможности предоставляются через интерфейсы базового класса. Возможно, вам просто нужно переосмыслить, зачем вам вообще нужно понижать цену.Если кто-то попадет сюда с boost :: shared_ptr ...
Вот как вы можете выполнить приведение к производному Boost shared_ptr. Предполагая, что Derived наследуется от Base.
Убедитесь, что в базовом классе / структуре есть хотя бы одна виртуальная функция. Также работает виртуальный деструктор.
источник