As it was the case in Boost, C++11 provides some functions for casting shared_ptr
:
std::static_pointer_cast std::dynamic_pointer_cast std::const_pointer_cast
I am wondering, however, why there are no equivalents functions for unique_ptr
.
Consider the following simple example:
class A { virtual ~A(); ... } class B : public A { ... } unique_ptr<A> pA(new B(...)); unique_ptr<A> qA = std::move(pA); // This is legal since there is no casting unique_ptr<B> pB = std::move(pA); // This is not legal // I would like to do something like: // (Of course, it is not valid, but that would be the idea) unique_ptr<B> pB = std::move(std::dynamic_pointer_cast<B>(pA));
Is there any reason why this usage pattern is discouraged, and thus, equivalent functions to the ones present in shared_ptr
are not provided for unique_ptr
?
In short: Use unique_ptr when you want a single pointer to an object that will be reclaimed when that single pointer is destroyed. Use shared_ptr when you want multiple pointers to the same resource.
It can be assigned: class owner { std::unique_ptr<someObject> owned; public: owner() { owned=std::unique_ptr<someObject>(new someObject()); } };
An explicit delete for a unique_ptr would be reset() . But do remember that unique_ptr are there so that you don't have to manage directly the memory they hold. That is, you should know that a unique_ptr will safely delete its underlying raw pointer once it goes out of scope.
A unique_ptr can only be moved. This means that the ownership of the memory resource is transferred to another unique_ptr and the original unique_ptr no longer owns it. We recommend that you restrict an object to one owner, because multiple ownership adds complexity to the program logic.
In addition to Mark Ransom's answer, a unique_ptr<X, D>
might not even store an X*
.
If the deleter defines the type D::pointer
then that's what is stored, and that might not be a real pointer, it only needs to meet the NullablePointer
requirements and (if unique_ptr<X,D>::get()
is called) have an operator*
that returns X&
, but it isn't required to support casting to other types.
unique_ptr
is quite flexible and doesn't necessarily behave very much like a built-in pointer type.
As requested, here is an example where the stored type is not a pointer, and therefore casting is not possible. It's a bit contrived, but wraps a made-up database API (defined as a C-style API) in a C++ RAII-style API. The OpaqueDbHandle type meets the NullablePointer
requirements, but only stores an integer, which is used as a key to lookup the actual DB connection via some implementation-defined mapping. I'm not showing this as an example of great design, just as an example of using unique_ptr
to manage a non-copyable, movable resource which is not a dynamically-allocated pointer, where the "deleter" doesn't just call a destructor and deallocate memory when the unique_ptr
goes out of scope.
#include <memory> // native database API extern "C" { struct Db; int db_query(Db*, const char*); Db* db_connect(); void db_disconnect(Db*); } // wrapper API class OpaqueDbHandle { public: explicit OpaqueDbHandle(int id) : id(id) { } OpaqueDbHandle(std::nullptr_t) { } OpaqueDbHandle() = default; OpaqueDbHandle(const OpaqueDbHandle&) = default; OpaqueDbHandle& operator=(const OpaqueDbHandle&) = default; OpaqueDbHandle& operator=(std::nullptr_t) { id = -1; return *this; } Db& operator*() const; explicit operator bool() const { return id > 0; } friend bool operator==(const OpaqueDbHandle& l, const OpaqueDbHandle& r) { return l.id == r.id; } private: friend class DbDeleter; int id = -1; }; inline bool operator!=(const OpaqueDbHandle& l, const OpaqueDbHandle& r) { return !(l == r); } struct DbDeleter { typedef OpaqueDbHandle pointer; void operator()(pointer p) const; }; typedef std::unique_ptr<Db, DbDeleter> safe_db_handle; safe_db_handle safe_connect(); int main() { auto db_handle = safe_connect(); (void) db_query(&*db_handle, "SHOW TABLES"); } // defined in some shared library namespace { std::map<int, Db*> connections; // all active DB connections std::list<int> unused_connections; // currently unused ones int next_id = 0; const unsigned cache_unused_threshold = 10; } Db& OpaqueDbHandle::operator*() const { return connections[id]; } safe_db_handle safe_connect() { int id; if (!unused_connections.empty()) { id = unused_connections.back(); unused_connections.pop_back(); } else { id = next_id++; connections[id] = db_connect(); } return safe_db_handle( OpaqueDbHandle(id) ); } void DbDeleter::operator()(DbDeleter::pointer p) const { if (unused_connections.size() >= cache_unused_threshold) { db_disconnect(&*p); connections.erase(p.id); } else unused_connections.push_back(p.id); }
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With