I don’t really understand the difference between a shared_ptr and the new handle notation (^) in C++/CX. From what I’ve read they seem to do the same thing regarding reference counting and memory management. What am I missing?
std::shared_ptr<Type>
//vs
Type^
Solely considering lifetime management, these are the same: a
shared_ptr<T>holds a strong (owning) reference to aTobject; aT^does the same.make_shared<T>is roughly equivalent toref new Tin C++/CX.If everywhere you see a
T^you thinkshared_ptr<T>orComPtr<T>orCComPtr<T>, then that’s okay–the lifetime management is roughly the same.How lifetime management works under the hood is different, though: every
Ttype for whichT^is well-formed is a Windows Runtime reference type that implements theIUnknowninterface, so theTobject is internally reference counted(*).shared_ptr<T>supports arbitrary types and uses external reference counting (i.e., it allocates its own reference counting mechanism to control the lifetime of the object).For weak references,
shared_ptr<T>hasweak_ptr<T>, andT^hasWeakReference.WeakReferenceis not strongly-typed, but you can easily write a strongly-typed reference wrapper around it. Otherwise, weak references work as you would expect them to. Support for weak references is optional: not all reference types support weak references, but most do.(*) There is one exception:
Platform::String^, which is not a Windows Runtime reference type, but is handled specially for a variety of reasons. You can think of it as being the same as any otherT^with respect to lifetime management, though.So, why do Windows Runtime types wear hats in C++/CX? Why isn’t a library solution like
shared_ptr<T>orComPtr<T>used?It’s because you never really have a pointer (or a hat) to a concrete runtime type: you can only interact with an object via a pointer to one of the interfaces that its type implements. Windows Runtime also does not support interface or class inheritance: every interface must derive directly from
IInspectable, and class inheritance is emulated through the use of COM aggregation.In short, there’s no library solution that would result in natural looking C++ code with static type safety. Function calls, derived-to-base conversions, and interface conversions usually require a call to
QueryInterfaceto get the right interface pointer.You can do this with a library solution (see, for example, the WRL library, or pretty much any COM code), but you can’t support C++ language features like implicit conversions or
dynamic_cast. Without the hats, you’re stuck dealing solely with interface pointers and having to callQueryInterfaceyourself.(If you’re interested in the rationale behind why the C++/CX language extension were developed and how the C++/CLI syntax ended up being selected for reuse, I’d recommend Jim Springfield’s post on this blog from last year, “Inside the C++/CX Design”. Also of note is episode 3 of GoingNative, in which Marian Luparu discusses C++/CX.)