En C++11, vous pouvez utiliser un shared_ptr<>
d'établir une relation de propriété avec un objet ou une variable et weak_ptr<>
pour référencer en toute sécurité cet objet de manière non propriétaire.
Vous pouvez également utiliser unique_ptr<>
établir une relation de propriété avec un objet ou une variable. Mais qu'en est-il si d'autres objets, non propriétaires, veulent également faire référence à cet objet ? weak_ptr<>
n'est pas utile dans ce cas. Les pointeurs bruts sont utiles mais présentent divers inconvénients (par exemple, ils peuvent être automatiquement initialisé à nullptr mais cela se fait par le biais de techniques qui ne sont pas cohérentes avec la std::*_ptr<>
types).
Quel est l'équivalent de weak_ptr<>
pour les références non propriétaires à des objets possédés via unique_ptr<>
?
Voici un exemple éclairant qui ressemble à quelque chose dans un jeu sur lequel je travaille.
class World
{
public:
Trebuchet* trebuchet() const { return m_trebuchet.get(); }
private:
std::unique_ptr< Trebuchet > m_trebuchet;
};
class Victim
{
public:
Victim( Trebuchet* theTrebuchet ) : m_trebuchet( theTrebuchet ) {}
~Victim()
{
delete m_trebuchet; // Duh. Oops. Dumb error. Nice if the compiler helped prevent this.
}
private:
Trebuchet* m_trebuchet; // Non-owning.
};
shared_ptr< Victim > createVictim( World& world )
{
return make_shared< Victim >( world.trebuchet() );
}
Ici, nous utilisons un pointeur brut pour maintenir une relation de non possession avec un objet possédé par l'intermédiaire de unique_ptr<>
ailleurs. Mais le brut est-il le mieux que nous puissions faire ?
L'espoir est un type de pointeur qui :
- Il ressemble aux autres types de pointeurs modernes. Par exemple
std::raw_ptr<T>
. - Remplace les pointeurs bruts afin qu'une base de code qui utilise des types de pointeurs modernes partout puisse trouver todo via une recherche de
_ptr<
(en gros). - Auto-initialise à nullptr.
Ainsi :
int* p; // Unknown value.
std::raw_ptr< int > p; // null.
Ce type existe-t-il déjà en C++, est-il proposé pour l'avenir, ou une autre implémentation est-elle largement disponible dans Boost, par exemple ?
11 votes
unique_ptr::get
si vous voulez accéder au pointeur sous-jacent. Il n'y a pas deweak_ptr
équivalent car alors leunique_ptr
ne serait pas très unique6 votes
Il n'y a rien de tel parce que
unique_ptr
est conçu pour ne pas avoir de surcharge par rapport à un pointeur brut. S'il devait garder un refcount de tous les pointeurs faibles, cela ne serait pas possible.1 votes
@Xeo, c'est juste. A
weak_ptr
équivalent pourunique_ptr
pourrait avoir un coût excessif. Mais ne pourrait-il pas y avoir une sorte de fonctionnalité intermédiaire : un type de pointeur syntaxiquement similaire aux autres types de pointeurs modernes, avec annulation automatique à la construction, prévention des appels directs à la suppression, etc. Ces types de pointeurs seraient vraisemblablement toujours sujets aux problèmes de pointeurs pendants, mais ils ne seraient pas pires - et même meilleurs - que les pointeurs bruts. Un tel type existe-t-il ?0 votes
@Old : Comment notifier les pointeurs non propriétaires sans surcharge ?
2 votes
@OldPeculier Vous confondez la sémantique de la propriété.
unique_ptr
est destiné à être un pointeur propriétaire. Si vous voulez un pointeur auquel d'autres peuvent avoir des références non propriétaires, utilisezshared_ptr
. À quoi servirait un autre pointeur intelligent quelque part entre les deux ?0 votes
@BillyONeal Vous ne le feriez pas. A
weak_ptr
-comme un pointeur non propriétaire serait bien, mais vous avez raison : cela entraînerait des dépenses. Mais ce n'est pas la seule option. Voir l'exemple de clarification dans la question, ainsi que mes autres commentaires pour plus de clarté .1 votes
@OldPeculier Si vous voulez vraiment un pointeur intelligent qui se comporte comme une
unique_ptr
mais qui n'est pas propriétaire, vous pouvez préparez-en un vous-même en fournissant un suppresseur qui ne fait rien. Cependant, je ne vois pas l'utilité d'une telle chose. Un pointeur brut est tout ce qui est nécessaire à la place.0 votes
@Praetorian Je comprends votre point de vue. Peut-être que j'essaie d'avoir le beurre et l'argent du beurre : éviter les frais généraux de
shared_ptr
yweak_ptr
mais prévoient des pointeurs non propriétaires. Pourtant, on ne peut pas travailler longtemps avecunique_ptr
avant de vouloir offrir des pointeurs limités et non propriétaires vers le même objet, et le pointeur brut semble, eh bien, très brut pour cet objectif.0 votes
@Praetorian Non, il n'y a aucun doute sur le fait de vouloir
unique_ptr
pour avoir une sémantique de la propriété. Cela n'aurait aucun sens. La question est de savoir comment faire référence à des objets appartenant à un objetunique_ptr
à travers d'autres pointeurs non propriétaires. Est-ce que le brut est le mieux qu'on puisse faire ? C'est la question.2 votes
Votre "Duh. Oops. Dumb error" n'est pas très convaincant. Vous ne devriez pas utiliser
delete
en dehors des poignées de propriété (voir aussi : Règle du zéro ). Se protéger contre une telle erreur, c'est se protéger contre Machiavel, pas contre Murphy.1 votes
@OldPeculier à part "se sentir cru", qu'y a-t-il à améliorer sur un pointeur cru ? Vous ne pouvez pas simplement demander quelque chose de mieux sans expliquer ce qui compte comme "mieux".
0 votes
@R.MartinhoFernandes Je travaille avec de nombreuses bases de code anciennes et importantes dans lesquelles l'utilisation moderne des pointeurs est introduite progressivement. Bien sûr, l'idéal serait de tout écrire à partir de zéro et d'interdire tout simplement les suppressions et autres sources de douleur brute des pointeurs. Dans la pratique, cependant, ces paradigmes coexistent, et ce type d'erreur est en fait courant. Une alternative au pointeur brut qui s'initialiserait à nullptr et empêcherait la suppression éviterait un ensemble assez large d'erreurs.
0 votes
Je ne vois pas en quoi cela peut aider, tout simplement parce que ces bases de code n'utilisent pas déjà un tel pointeur. Je suppose que vous ne pouvez pas simplement remplacer chaque pointeur brut par ce nouveau pointeur indélébile (sinon vous pourriez simplement rechercher toutes les instances de
delete
et s'en débarrasser), vous devez donc toujours évaluer chaque instance vous-même. Si vous faites un tel effort, pourquoi ne pas le corriger pour de bon et passer l'ensemble du code à un style moderne ?0 votes
Dans tous les cas, si vous voulez une classe qui s'initialise à nullptr et empêche la suppression, alors non, il n'y en a pas. Vous devez l'écrire vous-même.