Разница между std::shared_ptr‹Type› и Type^

Я не очень понимаю разницу между shared_ptr и новой нотацией дескриптора (^) в C++/CX. Из того, что я читал, кажется, что они делают то же самое в отношении подсчета ссылок и управления памятью. Что мне не хватает?

std::shared_ptr<Type>
//vs
Type^

person psousa    schedule 24.08.2012    source источник
comment
Удалены теги [C++] и [C++11], поскольку синтаксис Type^ не является правильным ни в одной из двух версий языка, что приводит к очевидному ответу: Разница в том, что одна из них является конструкцией C++11. а другой нет   -  person David Rodríguez - dribeas    schedule 24.08.2012
comment
@DavidRodríguez-dribeas: Похоже, теги не были удалены? В любом случае, я был бы склонен оставить теги как есть. Принимая во внимание, что T^ — это функция C++/CX, std::shared_ptr<T> — это функция C++, и вопрос касается обоих.   -  person James McNellis    schedule 24.08.2012
comment
@JamesMcNellis: Похоже, что теги не были удалены, не так ли? :) Если вы считаете, что они должны быть там, меня это устраивает. Мое понимание (которое может быть связано с простым невежеством) заключается в том, что std::shared_ptr доступен в c++/CX (т.е. вопрос не в сравнении функций на разных языках, а скорее в сравнении двух функций, доступных в одном конкретном языке.   -  person David Rodríguez - dribeas    schedule 24.08.2012


Ответы (2)


Исключительно с точки зрения управления временем жизни, они одинаковы: shared_ptr<T> содержит строгую (владеющую) ссылку на объект T; T^ делает то же самое. make_shared<T> примерно эквивалентно ref new T в C++/CX.

Если везде, где вы видите T^, вы думаете, что это shared_ptr<T>, ComPtr<T> или CComPtr<T>, то все в порядке — управление временем жизни примерно одинаково.

Однако внутреннее управление временем жизни работает иначе: каждый тип T, для которого T^ имеет правильный формат, является эталонным типом среды выполнения Windows, реализующим интерфейс IUnknown, поэтому объект T имеет внутренний подсчет ссылок(*). shared_ptr<T> поддерживает произвольные типы и использует внешний подсчет ссылок (т. е. выделяет собственный механизм подсчета ссылок для управления временем жизни объекта).

Для слабых ссылок shared_ptr<T> имеет weak_ptr<T>, а T^ имеет WeakReference. WeakReference не является строго типизированным, но вы можете легко написать вокруг него строго типизированную ссылочную оболочку. В противном случае слабые ссылки работают так, как вы ожидаете. Поддержка слабых ссылок необязательна: не все типы ссылок поддерживают слабые ссылки, но большинство.

(*) Существует одно исключение: Platform::String^, который не является ссылочным типом среды выполнения Windows, но обрабатывается специально по ряду причин. Однако вы можете считать его таким же, как и любой другой T^ в отношении управления жизненным циклом.


Итак, почему типы среды выполнения Windows носят шляпы в C++/CX? Почему не используется такое библиотечное решение, как shared_ptr<T> или ComPtr<T>?

Это потому, что у вас никогда не будет указателя (или шляпы) на конкретный тип среды выполнения: вы можете взаимодействовать с объектом только через указатель на один из интерфейсов, которые реализует его тип. Среда выполнения Windows также не поддерживает наследование интерфейса или класса: каждый интерфейс должен быть производным непосредственно от IInspectable, а наследование классов эмулируется с помощью агрегации COM.

Короче говоря, не существует библиотечного решения, которое привело бы к естественно выглядящему коду C++ со статической безопасностью типов. Вызовы функций, преобразования из производных в базовые и преобразования интерфейсов обычно требуют вызова QueryInterface для получения правильного указателя интерфейса.

Вы можете сделать это с помощью библиотечного решения (см., например, библиотеку WRL или почти любой код COM), но вы не можете поддерживать такие функции языка C++, как неявные преобразования или dynamic_cast. Без шляп вы застряли, имея дело исключительно с указателями интерфейса и должны сами вызывать QueryInterface.


(Если вам интересно, почему было разработано почему расширение языка C++/CX и как синтаксис C++/CLI был выбран для повторного использования, я бы порекомендовал запись Джима Спрингфилда в этом блоге. прошлого года, "Внутри дизайна C++/CX". следует отметить эпизод 3 GoingNative, в котором Мариан Лупару обсуждает C++/CX.)

person James McNellis    schedule 24.08.2012
comment
Какой отличный ответ... Большое спасибо, Джеймс. - person psousa; 24.08.2012
comment
@ Джеймс Макнеллис Спасибо. Пост Спрингфилда был полезен при изучении того, как мой опыт работы с C++ должен вписываться в разработку приложений, ориентированных на RT. Тем не менее, такой спонтанный ответ является большим подспорьем в освоении новой платформы. - person Chawathe Vipul S; 17.03.2013

Насколько я могу судить, в последнем отсутствует поддержка слабых ссылок и пользовательских функций освобождения.

Обратите внимание, что первый, будучи более общим, принимает любой тип (в принципе) и для безопасности и чистоты требует использования вспомогательной функции make_shared. Последнее поддерживается на уровне языка. Это означает, что такой код безопасен в C++/CX:

some_function(ref new foo(), ref new bar());

Находясь в С++, вам нужно сделать это:

// bad: if foo is allocated but bar's allocation throws, you leak!
some_function(new foo(), new bar());

// good: both never make it anywhere but into a shared_ptr, no leaks
some_function(make_shared<foo>(), make_shared<bar>());

В остальном, конечно, они реализуют ту же концепцию. Если вы находитесь в стране C++/CX, используйте последний синтаксис для простоты и единообразия; если вы пытаетесь придерживаться стандартного C++ или оборачиваете существующую схему управления ресурсами в схему с подсчетом ссылок, вам подойдет первая.

person GManNickG    schedule 24.08.2012
comment
C++/CX имеет WeakReference. Подробнее в MSDN. - person James McNellis; 24.08.2012