M. Lidström et moi nous sommes disputés :)
L'affirmation de M. Lidström est qu'une construction shared_ptr<Base> p(new Derived);
ne nécessite pas que Base ait un destructeur virtuel:
Armen Tsirunyan : "Vraiment? Le shared_ptr nettoiera-t-il correctement? Pourriez-vous s'il vous plaît dans ce cas démontrer comment cet effet pourrait être mis en œuvre?"
Daniel Lidström : "Le shared_ptr utilise son propre destructeur pour supprimer l'instance Concrete. Ceci est connu sous le nom de RAII dans la communauté C ++. Mon conseil est que vous appreniez tout ce que vous pouvez sur RAII. Cela rendra votre codage C ++ tellement plus facile lorsque vous l'utiliserez RAII dans toutes les situations. "
Armen Tsirunyan : "Je connais RAII, et je sais aussi que finalement le destructeur shared_ptr peut supprimer le px stocké lorsque pn atteint 0. Mais si px avait un pointeur de type statique vers
Base
et un pointeur de type dynamique versDerived
, alors à moins d'Base
avoir un destructeur virtuel, cela entraînera un comportement indéfini. Corrigez-moi si je me trompe. "Daniel Lidström : "Le shared_ptr sait que le type statique est Concrete. Il le sait depuis que je l'ai passé dans son constructeur! Cela semble un peu magique, mais je peux vous assurer que c'est par conception et extrêmement agréable."
Alors, jugez-nous. Comment est-il possible (si c'est le cas) d'implémenter shared_ptr sans exiger que les classes polymorphes aient un destructeur virtuel? Merci d'avance
la source
shared_ptr<void> p(new Derived)
cela détruira également l'Derived
objet par son destructeur, qu'il le soitvirtual
ou non.shared_ptr<T>( (T*)new U() )
oùstruct U:T
ne fera pas la bonne chose (et cela peut être fait indirectement facilement, comme une fonction qui prend unT*
et est passé aU*
)Réponses:
Oui, il est possible d'implémenter shared_ptr de cette façon. Boost le fait et la norme C ++ 11 requiert également ce comportement. De plus, shared_ptr gère plus qu'un simple compteur de référence. Un soi-disant suppresseur est généralement placé dans le même bloc de mémoire qui contient également les compteurs de référence. Mais la partie amusante est que le type de ce suppresseur ne fait pas partie du type shared_ptr. C'est ce qu'on appelle "l'effacement de type" et c'est fondamentalement la même technique utilisée pour implémenter les "fonctions polymorphes" boost :: function ou std :: function pour cacher le type du foncteur réel. Pour que votre exemple fonctionne, nous avons besoin d'un constructeur basé sur un modèle:
template<class T> class shared_ptr { public: ... template<class Y> explicit shared_ptr(Y* p); ... };
Donc, si vous utilisez ceci avec vos classes Base et Derived ...
class Base {}; class Derived : public Base {}; int main() { shared_ptr<Base> sp (new Derived); }
... le constructeur basé sur un modèle avec Y = Derived est utilisé pour construire l'objet shared_ptr. Le constructeur a ainsi la possibilité de créer l'objet de suppression et les compteurs de référence appropriés et stocke un pointeur vers ce bloc de contrôle en tant que membre de données. Si le compteur de référence atteint zéro, le suppresseur créé précédemment et prenant en charge les dérivés sera utilisé pour éliminer l'objet.
La norme C ++ 11 a ce qui suit à propos de ce constructeur (20.7.2.2.1):
Et pour le destructeur (20.7.2.2.2):
(Je souligne l'utilisation de la police en gras).
la source
the upcoming standard also requires this behaviour
: (a) Quelle norme et (b) pouvez-vous fournir une référence (à la norme)?add a comment
. IMO, c'est plusBoost does this
quethe Standard requires
. Je ne pense pas que la norme l'exige d'après ce que je comprends. En parlant de l » exemple de @sellibitzeshared_ptr<Base> sp (new Derived);
, nécessite deconstructor
simplement demander d'delete Derived
être bien défini et bien formé. Pour la spécification dedestructor
, il existe également unp
, mais je ne pense pas que cela fasse référence aup
dans la spécification deconstructor
.Lorsque shared_ptr est créé, il stocke un objet de suppression à l' intérieur de lui-même. Cet objet est appelé lorsque shared_ptr est sur le point de libérer la ressource pointée. Puisque vous savez comment détruire la ressource au moment de la construction, vous pouvez utiliser shared_ptr avec des types incomplets. Celui qui a créé le shared_ptr y a stocké un suppresseur correct.
Par exemple, vous pouvez créer un suppresseur personnalisé:
void DeleteDerived(Derived* d) { delete d; } // EDIT: no conversion needed. shared_ptr<Base> p(new Derived, DeleteDerived);
p appellera DeleteDerived pour détruire l'objet pointé. L'implémentation le fait automatiquement.
la source
shared_ptr
comme attribut.Simplement,
shared_ptr
utilise une fonction de suppression spéciale créée par un constructeur qui utilise toujours le destructeur de l'objet donné et non le destructeur de Base, c'est un peu de travail avec la méta-programmation de modèle, mais cela fonctionne.Quelque chose comme ca
template<typename SomeType> shared_ptr(SomeType *p) { this->destroyer = destroyer_function<SomeType>(p); ... }
la source