2009-08-31 5 views
67

Обновление: shared_ptr в этом примере похоже на файл в Boost, но он не поддерживает shared_polymorphic_downcast (или dynamic_pointer_cast или static_pointer_cast, если на то пошло)!Downcasting shared_ptr <Base> to shared_ptr <Derived>?

Я пытаюсь инициализировать общий указатель на производный класс, не теряя при этом счетчик ссылок:

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 *. Тем не менее, я хочу функциональность, выраженную кодом (то есть, поддерживая подсчет ссылок при подавлении базового указателя). Моя первая мысль была, чтобы обеспечить оператор приведения в Базе, так что неявное преобразование в Derived может иметь место (для педантов: Я хотел бы проверить, что вниз бросок действительно, не волнуйтесь):

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

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

+0

Зачем вам нужен shared_ptr , а не shared_ptr ? – Bill

+2

Поскольку я хочу получить доступ к функциям в Derived, которые не находятся в базе, без клонирования объекта (я хочу, чтобы один объект ссылался на два общих указателя). Кстати, почему не работают литые операторы? –

ответ

45

Предполагаю, вы используете boost::shared_ptr ... Я думаю, вы хотите dynamic_pointer_cast или shared_polymorphic_downcast.

Однако для этого требуются полиморфные типы.

какой тип Base* const есть? const Base* Я понимаю, но Base* const? К чему относятся const?

  • const Base * является изменяемый указатель на постоянной Base.
  • Base const * является изменяемым указателем на константу Base.
  • Base * const является постоянным указателем на mutable 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 будет увеличен.

См (dead link):

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

+0

К сожалению, ваше решение зависит от функциональности Boost, которая была специально исключена из конкретной реализации shared_ptr, которую мы используем (не спрашивайте, почему). Что касается объяснения константы, теперь это имеет гораздо больше смысла. –

+3

За исключением реализации других конструкторов 'shared_ptr' (взяв' static_cast_tag' и 'dynamic_cast_tag'), вы не можете многое сделать. Все, что вы делаете вне 'shared_ptr', не сможет управлять пересчетом. - В «идеальном» дизайне OO вы всегда можете использовать базовый тип и никогда не должны знать и не заботиться о том, что такое производный тип, потому что вся его функциональность доступна через интерфейсы базового класса. Возможно, вам просто нужно переосмыслить, почему вам нужно сбрасывать в первую очередь. –

+1

@ Тим Сильвестр: но C++ не является «идеальным» языком OO! :-) down-casts имеют свое место в не совершенном языке OO –

55

Вы можете использовать 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.

+3

Я не понял с первой строки, что он не использует 'std :: shared_ptr'. Но из комментариев первого ответа я предположил, что он не использует boost, поэтому он может использовать 'std :: shared_ptr'. –

+0

ОК. Сожалею. Он должен лучше пояснить, что он использует обычную реализацию. –

3

Если кто-то получает здесь с буст :: 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; 

Убедитесь 'Base' класс/структура имеет по меньшей мере одну виртуальную функцию. Также работает виртуальный деструктор.

Смежные вопросы