La question se situe vraiment dans le titre: je suis curieux de savoir quelle est la raison technique de cette différence, mais aussi la raison?
std::shared_ptr<void> sharedToVoid; // legal;
std::unique_ptr<void> uniqueToVoid; // ill-formed;
La question se situe vraiment dans le titre: je suis curieux de savoir quelle est la raison technique de cette différence, mais aussi la raison?
std::shared_ptr<void> sharedToVoid; // legal;
std::unique_ptr<void> uniqueToVoid; // ill-formed;
C'est parce qu' std::shared_ptr
implémente type-erasure, tout en std::unique_ptr
ne le sont pas.
Depuis std::shared_ptr
implémente type-erasure, il prend également en charge une autre propriété intéressante, viz. il n'a pas besoin du type de la deleter comme le modèle type de l'argument de la classe de modèle. Regardez leurs déclarations:
template<class T,class Deleter = std::default_delete<T> >
class unique_ptr;
qui a Deleter
comme paramètre de type, tandis que
template<class T>
class shared_ptr;
ne pas l'avoir.
Maintenant, la question est, pourquoi est - shared_ptr
mettre en œuvre type-erasure? Eh bien, il le fait, car il a le soutien de comptage de références, et à l'appui de cela, il a d'allouer de la mémoire de tas et depuis il a pour allouer de la mémoire de toute façon, il va plus loin et met en œuvre des type-erasure — qui a besoin d'allocation de tas trop. Donc, fondamentalement, c'est juste d'être opportuniste!
En raison de l'effacement, std::shared_ptr
est capable de supporter deux choses:
void*
, mais il est encore capable de supprimer les objets sur la destruction correctement en invoquant leur destructeur.Alright. Qui est tout au sujet de combien de std::shared_ptr
travaux.
Maintenant, la question est, pouvez - std::unique_ptr
stocker des objets comme void*
? Eh bien, la réponse est oui , à condition que vous passer un adapté deleter comme argument. Voici une de ces manifestations:
int main()
{
auto deleter = [](void const * data ) {
int const * p = static_cast<int const*>(data);
std::cout << *p << " located at " << p << " is being deleted";
delete p;
};
std::unique_ptr<void, decltype(deleter)> p(new int(959), deleter);
} //p will be deleted here, both p ;-)
De sortie (démo en ligne):
959 located at 0x18aec20 is being deleted
Vous avez posé une question très intéressante dans le commentaire:
Dans mon cas, j'ai besoin d'un type d'effacement deleter, mais il semble possible (au prix de quelques tas de répartition). En gros, est-ce à dire qu'il n'y est en fait une niche place pour un 3ème type de pointeur intelligent: une propriété exclusive de pointeur intelligent avec le type d'effacement.
pour ce qui @Steve Jessop a suggéré la solution suivante,
Je ne l'ai jamais essayé, mais peut-être que vous pourriez réaliser que par un
std::function
comme le deleter typeunique_ptr
? Supposant que cela fonctionne réellement, alors vous êtes fait, la propriété exclusive et un type effacé deleter.
Suite à cette suggestion, je l'ai fait (bien qu'il ne fait pas usage de l' std::function
comme il ne semble pas nécessaire):
using unique_void_ptr = std::unique_ptr<void, void(*)(void const*)>;
template<typename T>
auto unique_void(T * ptr) -> unique_void_ptr
{
return unique_void_ptr(ptr, [](void const * data) {
T const * p = static_cast<T const*>(data);
std::cout << "{" << *p << "} located at [" << p << "] is being deleted.\n";
delete p;
});
}
int main()
{
auto p1 = unique_void(new int(959));
auto p2 = unique_void(new double(595.5));
auto p3 = unique_void(new std::string("Hello World"));
}
De sortie (démo en ligne):
{Hello World} located at [0x2364c60] is being deleted.
{595.5} located at [0x2364c40] is being deleted.
{959} located at [0x2364c20] is being deleted.
Espérons que cela aide.
L'une des justifications est dans l'un des nombreux cas d'utilisation d'un shared_ptr
- à savoir une durée de vie de l'indicateur ou de la sentinelle.
Cela a été mentionné dans l'original stimuler la documentation:
auto register_callback(std::function<void()> closure, std::shared_ptr<void> pv)
{
auto closure_target = { closure, std::weak_ptr<void>(pv) };
...
// store the target somewhere, and later....
}
void call_closure(closure_target target)
{
// test whether target of the closure still exists
auto lock = target.sentinel.lock();
if (lock) {
// if so, call the closure
target.closure();
}
}
Où closure_target
est quelque chose comme ceci:
struct closure_target {
std::function<void()> closure;
std::weak_ptr<void> sentinel;
};
L'appelant inscrire un rappel de quelque chose comme ceci:
struct active_object : std::enable_shared_from_this<active_object>
{
void start() {
event_emitter_.register_callback([this] { this->on_callback(); },
shared_from_this());
}
void on_callback()
{
// this is only ever called if we still exist
}
};
parce qu' shared_ptr<X>
est toujours convertibles shared_ptr<void>
, le event_emitter peut maintenant être au courant du type d'objet, il est de retour d'appel.
Cet arrangement communiqués abonnés à l'événement de l'émetteur de l'obligation de la manipulation de la traversée des cas (si la fonction de rappel dans une file d'attente, en attente d'être traitées tout en active_object s'en va?), et aussi signifie qu'il n'est pas nécessaire de synchroniser le désabonnement. weak_ptr<void>::lock
est un mode synchrone.
Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.