2009-09-02 5 views
5

Умные указатели обрабатывают литье, а если нет, то безопасный способ обойти это ограничение?Как следует использовать умные указатели?

Пример того, что я пытаюсь сделать, это наличие двух векторов STL (например), содержащих интеллектуальные указатели. Первый содержит интеллектуальные указатели на базовый класс, а второй содержит интеллектуальные указатели на производный класс. Смарт-указатели ссылаются на подсчет, например. аналогичное поведение с shared_ptrs Boost, но ручная работа. Я включил некоторые примеры кода, которые я взбитыми привести пример:

vector<CBaseSmartPtr> vecBase; 
vector<CDerivedSmartPtr> vecDer; 
... 
CBaseSmartPtr first = vecBase.front(); 
vecDer.push_back(CDerivedSmartPtr(dynamic_cast<CDerived*>(first.get())); 

Это кажется не безопасной для меня, как я думаю, что я в конечном итоге с двумя смарт-указатели управления один и тот же объект. В какой-то момент вниз по дорожке это, вероятно, приведет к тому, что один из них освободит объект, в то время как другой все еще содержит ссылки на него.

Что бы я надеялся, но не думаю, что это сработает, это прямой литой при сохранении того же объекта, например.

dynamic_cast<CDerivedSmartPtr>(first) 

Должен ли я смотреть, чтобы изменить второй контейнер также использовать CBaseSmartPtr и опущенными только на использование? Существуют ли другие решения?

+2

Почему вы сделать свой собственный? – GManNickG

+1

Я думаю, что это интересный вопрос, потому что он попадает во внутренние реализации интеллектуальных указателей. Но я также хотел бы отметить, что создание downcasting в вашей стратегии подразумевает недостаток дизайна, IMHO. –

+0

Чтобы ответить, почему я делаю свой собственный, это устаревший код и вырвать текущее использование и заменить его на Boost, было бы довольно большим изменением. Это в моем списке «будущих вещей, чтобы посмотреть». – dlanod

ответ

5

Умные указатели могут обрабатывать downcasting, но это не автоматическое. И получение const-correctness в может быть немного сложным (я использовал нашу интеллектуальную реализацию указателя в вопросах собеседования, есть некоторые шаблонные обманки). Но многие пользователи интеллектуальных указателей никогда не создают своих интеллектуальных указателей со стандартными типами.

Первое, что вам нужно для правильного ответа - это счетчик. Поскольку вам может потребоваться предоставить счетчик между smart_ptr<Base> и smart_ptr<Derived>, тип счетчика не должен зависеть от аргумента типа. В общем, это не имеет большого значения. Счетчик - это просто size_t, возможно, завернутый в класс. (Примечание: существуют альтернативные варианты умных указателей, но вопрос настоятельно предлагает использовать счетчик)

Приведение к основанию должно быть довольно тривиальным. Следовательно, ваш smart_ptr должен иметь конструктор с smart_ptr. В этом ctor добавьте строку static_cast<T*>((U*)0);. Это не генерирует код, но предотвращает создание экземпляра, когда T не является базой U (по модулю const).

Другой способ должен быть явным. Вы не можете программно перечислить все базы T, поэтому smart_ptr<T> не может быть получен из smart_ptr<Base1_of_T>, smart_ptr<Base2_of_T>, ... Следовательно, dynamic_cast<smart_ptr<T> > не будет работать. Вы можете предоставить свой собственный smart_dynamic_cast<SPT>(smart_ptr<U> const& pU). Это лучше всего реализовать в качестве функции, сохраняющей SPT. В этой функции вы можете просто сделать return SPT(dynamic_cast<SPT::value_type*>(&*pU)).

+0

На самом деле, я думаю, вы cann программно перечисляете базы. Разве Андрей не показал нам, как это сделать? – sbi

+0

Получил книгу, но не здесь. Я не помню ничего подобного. Просто чтобы устранить любую путаницу: целью «перечисления баз» является определение набора типов B1..Bn, заданного типа D, так что отношение 'is_base_and_derived ' выполняется для всех типов B1..Bn и других типов. – MSalters

+0

@MSalters: Ах мой мозг. Я думал о перечислении типов и создании их базового класса ... Извините. – sbi

0

Обычные смарт-указатели, такие как std::auto_ptr, небезопасны для использования в контейнерах STL из-за перемещения владельца, когда STL назначает экземпляры интеллектуальных указателей друг другу, когда он копирует данные внутри. Вместо этого вы должны использовать что-то вроде boost::shared_ptr, которое внутренне реализует подсчет ссылок, чтобы гарантировать, что объект остается в живых независимо от того, сколько экземпляров умных указателей ссылаются на него. Если вы пишете собственные типы интеллектуальных указателей, вам необходимо реализовать аналогичный подсчет ссылок.

+0

Привет, я добавлю комментарий к вопросу, в котором явствует, что я планирую использовать ссылки, подсчитанные на интеллектуальные указатели. – dlanod

2

Имущество, которое вы хотите, - это ковариация в указанном типе. То есть, если D isa B, то вы хотите smartptr<D> isa smartptr<B>. Я не думаю, что это элегантно поддерживается на C++, но, как всегда, доступны шаблоны/перегрузки.

http://www.boost.org/doc/libs/1_39_0/libs/smart_ptr/pointer_cast.html дает динамическое приведение, которое работает на регулярной основе и повышает :: smart_ptr. Вы должны учиться на практике, если не хотите использовать Boost.

+4

+1 для 'dynamic_pointer_cast' и -1 для того, чтобы сказать, что C++ не имеет ковариантных типов возврата. См. Здесь: http://www.parashift.com/c++-faq-lite/virtual-functions.html#faq-20.8 –

+0

@ Кристо: Я с вами на обоих, но я все еще думаю, что это очень хороший совет. @ wang-wang: Почему бы вам не удалить ложную инструкцию? Это облегчило бы другим, чтобы поддержать это, в противном случае хорошее, ответ. – sbi

+0

Вы правы - я не знал, что виртуальные методы допускают ковариантные типы возврата! Но верно, что общие типы, например. ptr vector не поддерживают ковариацию (например, Java, хотя и по типу стирания). –

1

Следуйте за темой here в одном из списков рассылки. Он показывает, как можно реализовать понижающее преобразование интеллектуального указателя в случае boost :: shared_ptr. HTH

+0

Это выглядит просто страшно. Он отличает shared_ptr * к shared_ptr *. См. Также ответ Райнера Дейка. – MSalters

+0

Хммм, да. Спасибо за обновление. – Abhay

0

Я нашел это на страницах Microsoft:

std::shared_ptr<base> sp0(new derived); 
    std::shared_ptr<derived> sp1 = 
    std::dynamic_pointer_cast<derived>(sp0); 
Смежные вопросы