Mark Ransom のanswerに加えて、 aunique_ptr<X, D>
は an を保存することさえできないかもしれませんX*
。
デリータがタイプを定義する場合、D::pointer
それが格納され、それは実際のポインタではない可能性があります。NullablePointer
要件を満たすだけでよく、(unique_ptr<X,D>::get()
が呼び出された場合)operator*
を返すX&
が必要ですが、他のへのキャストをサポートする必要はありません。種類。
unique_ptr
非常に柔軟で、必ずしも組み込みポインター型のように振る舞うとは限りません。
リクエストに応じて、格納された型がポインターではないため、キャストができない例を次に示します。これは少し不自然ですが、(C スタイル API として定義された) 作成されたデータベース API を C++ RAII スタイル API でラップします。OpaqueDbHandle 型はNullablePointer
要件を満たしていますが、実装定義のマッピングを介して実際の DB 接続を検索するためのキーとして使用される整数のみを格納します。私はこれを素晴らしいデザインの例として示しているのunique_ptr
ではなく、動的に割り当てられたポインターではない、コピー不可能で移動可能なリソースを管理するために使用する例として、「deleter」がデストラクタを呼び出すだけでなく、unique_ptr
が範囲外になったときにメモリの割り当てを解除します。
#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);
}