En plus de l'article de Mark Ransom réponse , a unique_ptr<X, D>
peut même ne pas stocker un X*
.
Si le suppresseur définit le type D::pointer
alors c'est ce qui est stocké, et il se peut que ce ne soit pas un vrai pointeur, il doit seulement répondre à l'exigence de la norme de l'UE. NullablePointer
et (si unique_ptr<X,D>::get()
est appelé) ont un operator*
qui renvoie X&
mais il n'est pas nécessaire de prendre en charge le moulage vers d'autres types.
unique_ptr
est assez flexible et ne se comporte pas nécessairement comme un type de pointeur intégré.
Comme demandé, voici un exemple où le type stocké n'est pas un pointeur, et où le casting n'est donc pas possible. C'est un peu artificiel, mais cela enveloppe une API de base de données inventée (définie comme une API de style C) dans une API de style C++ RAII. Le type OpaqueDbHandle répond aux critères suivants NullablePointer
mais ne stocke qu'un nombre entier, qui est utilisé comme clé pour rechercher la connexion réelle à la base de données via un mappage défini par l'implémentation. Je ne présente pas ce cas comme un exemple de bonne conception, mais simplement comme un exemple d'utilisation de la fonction unique_ptr
pour gérer une ressource non copiable et mobile qui n'est pas un pointeur alloué dynamiquement, où le "suppresseur" ne se contente pas d'appeler un destructeur et de désallouer la mémoire lorsque l'objet unique_ptr
sort du champ d'application.
#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);
}
4 votes
Si le cast dynamique échoue, voulez-vous que l'objet précédemment possédé soit détruit ?
2 votes
Si l'objet pointé par
pA
n'est pas convertible en typeB
(c'est-à-dire,dynamic_cast<B>(pA.get())
échoue), que voulez-vous qu'il arrive à l'objet ? Est-ce quepA
conserver la propriété ? Doit-il être détruit ?2 votes
CharlesBailey C'est en fait un bon point. Il s'agit en fait d'une décision de mise en œuvre importante. Probablement que si
dynamic_cast
échoue, le "bon sens" conseillerait d'abandonner le casting, sans modifier le pointeur d'origine. C'est en fait le comportement de la réponse de cdhowie.