Une simple mise en œuvre de Pointeur Intelligent de la Classe
Dans le livre C++ Primer 13.5.1
, mettre en place un Pointeur Intelligent de la Classe à l'aide d'un Utilisez-le Comte de Classe. Leur mise en œuvre est comme suit:
-
Utilisez-Le Comte De Classe
//private class for use by HasPtr only class U_Ptr { friend class HasPtr; int *ip; size_t use; U_Ptr(int *p): ip(p), use(1) { } ~U_Ptr() { delete ip; } };
-
Pointeur Intelligent De La Classe
/* smart pointer class: takes ownership of the dynamically allocated object to which it is bound User code must dynamically allocate an object to initialize a HasPtr and must not delete that object; the HasPtr class will delete it */ class HasPtr { public: //HasPtr owns the pointer; p must have been dynamically allocated HasPtr(int *p, int i) : ptr(new U_Ptr(p)), val(i) { } //copy members and increment the use count HasPtr(const HasPtr &orig) : ptr(orig.ptr), val(orig.val) { ++ptr->use; } HasPtr& operator=(const HasPtr&); //if use count goes to zero, delete the U_Ptr object ~HasPtr() { if (--ptr->use == 0) delete ptr; } friend ostream& operator<<(ostream&, const HasPtr&); //copy control and constructors as before //accessors must change to fetch value from U_Ptr object int *get_ptr() const { return ptr->ip; } int get_int() const { return val; } //change the appropriate data member void set_ptr(int *p) { ptr->ip = p; } void set_int(int i) { val = i; } //return or change the value pointed to, so ok for const objects //Note: *ptr->ip is equivalent to *(ptr->ip) int get_ptr_val() const { return *ptr->ip; } void set_ptr_val(int i) { *ptr->ip = i; } private: U_Ptr *ptr; //points to use-counted U_Ptr class int val; };
Me demande: je suis curieux de savoir pourquoi ne pas tout simplement à l'aide d'un int *
agir comme le Use-Count Class
, tout comme le int* countPtr;
utilisé dans le nouveau Smart Pointer Class
:
class T
{
private:
int* countPtr; //
int* p;
int val;
public:
T(){
p = new int();
countPtr = new int();
*countPtr = 1;
val = 0;
}
T(T& t){
p = t.p;
countPtr = t.countPtr;
val = t.val;
*countPtr += 1;
}
T& operator = ( const T& rT){
if(*countPtr>1){
*countPtr -= 1;
}
else{
delete p;
delete countPtr;
}
p = rT.p;
countPtr = rT.countPtr;
val = rT.val;
*countPtr += 1;
return *this;
}
~T(){
if(*countPtr>1){
*countPtr -= 1;
}
else{
delete p;
delete countPtr;
}
}
int *get_ptr() const { return p; }
int get_int() const { return val; }
//change the appropriate data member
void set_ptr(int *ptr) { p = ptr; }
void set_int(int i) { val = i; }
};
Test: j'ai testé le ci-dessus Smart Pointer Class
aide d'un code comme le suivant, et il semble bien fonctionner.
int main()
{
T t1;
T t2(t1);
T t3(t1);
T t4;
t4 = t1;
return 0;
}
Vraie question: Est ce nouveau Smart Pointer Class
avec simplement un int *countPtr
suffisant? Si oui, pourquoi s'embêter à utiliser un Use-Count Class
comme dans le livre? Si non, que dois-je manquer?
Pourquoi avez-vous un
val
membre dans la classe?Tout comme le
val
dans la version originale, c'est à dire une variable membre de smart pointeur de la classe.Mais la variable n'a pas l'utilisation qui est visible ici.
Il semble que
val
est aussi mystérieusement ensemble, mais pas accessible dans l'original HasPtr
OriginalL'auteur herohuyongtao | 2014-01-03
Vous devez vous connecter pour publier un commentaire.
Une propriété de la mise en œuvre originale est que la
delete
est effectuée, dans le bloc de contrôle d'objet, avec l'original de type pointeur. C'est une partielle type d'effacement. Peu importe combien le pointeur intelligent objets sont copiés, avec un peu de types différents, l'origine du bloc de contrôle reste le même, avecdelete
via l'original de type pointeur.Cependant, depuis le code original pour vous montrer n'est pas basé sur un modèle, on doit supposer que c'est un exemple précoce, suivi plus tard par les mêmes basées sur des modèles de code.
De la conversion d'un pointeur dans une classe de base de la hiérarchie, comme cela peut arriver avec la copie d'un pointeur intelligent, signifie que
delete
sur le nouveau type de pointeur est valide uniquement si le connu statiquement nouveau type dispose d'un destructeur virtuel.Par exemple,
std::shared_ptr
supprime également (garanti) par l'intermédiaire de l'original de type pointeur, à moins que l'on fournit explicitement un deleter foncteur qui fait quelque chose d'autre.OriginalL'auteur Cheers and hth. - Alf
Votre code est équivalent à la norme code signalé par le livre. Toutefois, il est pire à certains égards:
vous avez besoin de deux allocations/deallocations au lieu d'une (deux ints au lieu d'un seul objet). Cela peut être plus lente et un peu plus difficile à gérer.
vous avez une copie du pointeur dupliqué dans chaque objet. Donc: la duplication des informations qui devraient vous garantissons valide.
votre objet est plus grand (deux pointeurs au lieu d'un)
Vous n'avez qu'une note positive:
OriginalL'auteur Emanuele Paolini
Ma conjecture est que l'auteur - consciemment ou inconsciemment - est conscient que d'avoir une catégorie distincte est utile dans le monde réel des pointeurs intelligents, par exemple:
un compte de la faiblesse des pointeurs (je ne sais pas si vous avez entendu parler d'eux encore - ils suivre un objet sans l'extension de sa durée de vie, de sorte que vous pouvez essayer de convertir un dans un (normal) pointeur partagé plus tard, mais il ne fonctionne que si il y a au moins un pointeur partagé de l'objet autour de l'ont maintenu en vie)
un mutex pour faire le pointeur partagé thread-safe (bien que les opérations atomiques peut-être mieux quand disponible),
des informations de débogage (par exemple, boost::shared_ptr a un #ifdef pour inclure une partagée compteur id)
virtuel expédition table, utilisés par exemple par les boost partagé des pointeurs vers d'expédition à OS-code approprié (voir boost/smart_ptr/detail/sp_counted_base_*.php des en-têtes)
Je ne connais pas le livre, mais peut-être qu'ils vont passer à l'expliquer à quoi d'autre pourrait aller dans
U_Ptr
....vrai, même si les opérations atomiques n'étaient pas dans le Standard de la bibliothèque jusqu'à ce que le C++11, donc un vieux livre de vouloir marcher le lecteur à travers une mise en œuvre pourrait les éviter. Je vais ajouter quelques autres exemples.
OriginalL'auteur Tony Delroy
(Mise à jour 14.4.2017)
J'ai moi-même essayé unique_ptr et shared_ptr et a été peu surpris de voir que ces classes ne pas rendre votre vie plus facile. J'ai eu de la fonction d'un API où la fonction serait de ramasser
Object*&
- remplir (pointeur), et après cela, vous devez supprimer cet objet. Il est possible d'utiliser le c++11, mais vous avez besoin d'ajouter supplémentaire temporaire pointeur à cette fin. (Afin que l'utilisation de *_ptr classes ne pas me rend la vie plus facile)Comment complexe serait-il à mettre en œuvre pointeur intelligent ?
Juste en jetant un coup d'oeil rapidement auto_ptr implémentation de la classe, j'ai rapidement développé intelligent simple point de conteneur de classe, mais ensuite j'ai remarqué que j'ai besoin d'un support pour plusieurs pointeurs intelligents référencer le même objet pointeur.
Ok, alors comment complexe pourrait-il être le code de comptage de référence - je, et suis allé sur google et trouvé un article intéressant à ce sujet:
http://www.codingwisdom.com/codingwisdom/2012/09/reference-counted-smart-pointers-are-for-retards.html
En quelque sorte, j'ai tendance à être d'accord avec l'auteur de cet article et les commentaires dans cet article que le comptage de référence rend la vie encore plus complexe, mais toujours en essayant de coller à la plaine de C aussi des sons peu stupide.
Je vais maintenant ajouter un extrait de code ici de ma propre classe, et si vous voulez obtenir la dernière version, vous pouvez vérifier dans ce référentiel svn: https://sourceforge.net/p/testcppreflect/code/HEAD/tree/SmartPtr.h
Ci-dessous est une version plus ancienne.
J'ai remplacé le comptage de référence avec une simple liste chaînée liste liée conserve toutes les instances de classe de référence, chaque destructeur supprime une référence, loin.
J'ai décidé de renommer l'opérateur* pour refptr() la fonction juste pour éviter les développeurs à écrire extra de fantaisie de code. ("C++ bijoux")
Donc, en général, je suis d'accord avec l'article ci-dessus - s'il vous plaît ne faites pas de pointeurs intelligents, trop intelligents. 🙂
Je suis libre de toute amélioration des suggestions concernant cette classe et le potentiel des corrections de bugs.
Et je voulais aussi répondre à l'auteur original de questions:
Vous êtes à l'aide d'une mécanique pour le comte de gestion, comme le lien de l'article ci-dessus mentionne - il devient non négligeable de suivre et de débogage de comptage de référence. Dans mon code snippet-je utiliser lié liste de pointeur intelligent instances, qui ne procède à aucune allocation ( donc de la mise en œuvre ci-dessus est plus rapide que tout autre pointeur intelligent de mise en œuvre ), aussi il est plus facile de débogage de pointeur intelligent lui-même - vous pouvez consulter par le lien (à côté) qui verrouille votre mémoire d'avoir été recueillis.
Mais dans l'ensemble - si vous rencontrez des fuites de mémoire, je dirais qu'il est hautement non-trivial de trouver où ils sont, si vous n'êtes pas à l'origine fait que le code. Smart pointeur de classe n'aide pas dans ce sens afin de savoir qui et comment beaucoup de choses ont filtré. Mieux le code une fois et correctement que de se battre avec votre propre bête plus tard.
Pour des fuites de mémoire, je vous recommande de trouver les outils existants et de les utiliser - par exemple celui-ci:
https://sourceforge.net/projects/diagnostic/
(Il y en a beaucoup, mais aucun d'entre eux fonctionne de manière fiable/assez bon).
Je sais que vous êtes impatients de mettre de l'aversion pour cette mise en œuvre, mais vraiment - s'il vous plaît dites-moi quels sont les obstacles que vous voyez dans cette mise en œuvre ?!
OriginalL'auteur TarmoPikaro