Comptage de référence en C++ OO-Style
Je suis tombé sur une intrigante de la mise en œuvre d'une classe de base sur la C++ FAQ que, selon ma compréhension naïve, peut servir comme une alternative à certains de pointeur intelligent implémentations (ex: shared_ptr). Voici l'exemple de code mot à mot, mais s'il vous plaît suivez le lien ci-dessus pour une explication:
class Fred {
public:
static Fred create1(std::string const& s, int i);
static Fred create2(float x, float y);
Fred(Fred const& f);
Fred& operator= (Fred const& f);
~Fred();
void sampleInspectorMethod() const; //No changes to this object
void sampleMutatorMethod(); //Change this object
...
private:
class Data {
public:
Data() : count_(1) { }
Data(Data const& d) : count_(1) { } //Do NOT copy the 'count_' member!
Data& operator= (Data const&) { return *this; } //Do NOT copy the 'count_' member!
virtual ~Data() { assert(count_ == 0); } //A virtual destructor
virtual Data* clone() const = 0; //A virtual constructor
virtual void sampleInspectorMethod() const = 0; //A pure virtual function
virtual void sampleMutatorMethod() = 0;
private:
unsigned count_; //count_ doesn't need to be protected
friend class Fred; //Allow Fred to access count_
};
class Der1 : public Data {
public:
Der1(std::string const& s, int i);
virtual void sampleInspectorMethod() const;
virtual void sampleMutatorMethod();
virtual Data* clone() const;
...
};
class Der2 : public Data {
public:
Der2(float x, float y);
virtual void sampleInspectorMethod() const;
virtual void sampleMutatorMethod();
virtual Data* clone() const;
...
};
Fred(Data* data);
//Creates a Fred smart-reference that owns *data
//It is private to force users to use a createXXX() method
//Requirement: data must not be NULL
Data* data_; //Invariant: data_ is never NULL
};
Fred::Fred(Data* data) : data_(data) { assert(data != NULL); }
Fred Fred::create1(std::string const& s, int i) { return Fred(new Der1(s, i)); }
Fred Fred::create2(float x, float y) { return Fred(new Der2(x, y)); }
Fred::Data* Fred::Der1::clone() const { return new Der1(*this); }
Fred::Data* Fred::Der2::clone() const { return new Der2(*this); }
Fred::Fred(Fred const& f)
: data_(f.data_)
{
++data_->count_;
}
Fred& Fred::operator= (Fred const& f)
{
//DO NOT CHANGE THE ORDER OF THESE STATEMENTS!
//(This order properly handles self-assignment)
//(This order also properly handles recursion, e.g., if a Fred::Data contains Freds)
Data* const old = data_;
data_ = f.data_;
++data_->count_;
if (--old->count_ == 0) delete old;
return *this;
}
Fred::~Fred()
{
if (--data_->count_ == 0) delete data_;
}
void Fred::sampleInspectorMethod() const
{
//This method promises ("const") not to change anything in *data_
//Therefore we simply "pass the method through" to *data_:
data_->sampleInspectorMethod();
}
void Fred::sampleMutatorMethod()
{
//This method might need to change things in *data_
//Thus it first checks if this is the only pointer to *data_
if (data_->count_ > 1) {
Data* d = data_->clone(); //The Virtual Constructor Idiom
--data_->count_;
data_ = d;
}
assert(data_->count_ == 1);
//Now we "pass the method through" to *data_:
data_->sampleMutatorMethod();
}
Je ne vois pas cette méthode est utilisée dans toutes les bibliothèques C++; bien qu'il semble très élégant. En supposant qu'un seul thread de l'environnement, par souci de simplicité, veuillez répondre aux questions suivantes:
- Est-ce une alternative viable à l'pointeur intelligent approche pour la gestion de la durée de vie des objets, ou est-il juste d'avoir des ennuis?
- Si c'est approprié, pourquoi supposez-vous qu'il n'est pas utilisé plus souvent?
D'un point de vue intéressant là - yosefk.com/c++apq/tas.html#fqa-16.26
Un bref coup d'oeil dans le code, il ressemble à pimpl/poignée-corps de pont/pour moi, (même si les gens l'habitude de mettre le "corps" de source séparée à la place, mais l'idée est similaire à ce que vous êtes en montrant). J'ai vu aussi pas mal de bibliothèques utilisant la même approche, avec fonction de comptage de référence. Il n'est pas rare en effet. (La lib que j'ai rencontré à l'aide de cet idiome le plus fortement est Roguewave Source Pro 😛 )
Qt et KDE faire un large usage de la PIMPL idiome afin de maintenir le binaire-la compatibilité des logiciels qui dépend de ces bibliothèques. Sans que le mécanisme mis en place, les développeurs seraient obligés de re-compiler leurs applications à chaque fois que quelque chose a changé sur le back-end de la bibliothèque, les implémentations de la classe.
Verax: Prix de la générosité de accepté de répondre!
Un bref coup d'oeil dans le code, il ressemble à pimpl/poignée-corps de pont/pour moi, (même si les gens l'habitude de mettre le "corps" de source séparée à la place, mais l'idée est similaire à ce que vous êtes en montrant). J'ai vu aussi pas mal de bibliothèques utilisant la même approche, avec fonction de comptage de référence. Il n'est pas rare en effet. (La lib que j'ai rencontré à l'aide de cet idiome le plus fortement est Roguewave Source Pro 😛 )
Qt et KDE faire un large usage de la PIMPL idiome afin de maintenir le binaire-la compatibilité des logiciels qui dépend de ces bibliothèques. Sans que le mécanisme mis en place, les développeurs seraient obligés de re-compiler leurs applications à chaque fois que quelque chose a changé sur le back-end de la bibliothèque, les implémentations de la classe.
Verax: Prix de la générosité de accepté de répondre!
OriginalL'auteur Verax | 2012-08-03
Vous devez vous connecter pour publier un commentaire.
Non, je ne pense pas que c'est une bonne idée de réinventer le comptage de référence, surtout depuis que nous avons std::shared_ptr maintenant en C++11. Vous pouvez facilement mettre en œuvre vos éventuellement polymorphe de référence compté Pimpl idiome de la classe en termes de std::shared_ptr. Remarquez comment nous n'avons pas à mettre en œuvre copier ctor, de cession, de dtor plus et la mutation devient plus simple.w.r.t. le compteur de référence et le clonage:
...et la mise en œuvre...
(non testé)
Je pense que le comptage de référence, si vous mettez en place vous-même, il est plus facile de se tromper. Il a aussi la réputation d'être lent dans multithread environnements, car les compteurs de référence doivent être incrémenté et décrémenté de manière atomique. Mais je suppose en raison de C++11 qui propose des shared_ptr et la sémantique de déplacement, cette copie sur écriture motif peut être un peu plus populaire encore. Si vous activez la sémantique de déplacement pour le Fred classe, vous pouvez éviter une partie des coûts de atomiquement de l'incrémentation des compteurs de référence. Donc le déplacement d'un objet Fred d'un endroit à un autre devrait être encore plus rapide que de copier.
OriginalL'auteur sellibitze
C'est une alternative, mais à moins d'avoir une très bonne raison pour l'utiliser, il est juste de réinventer la roue (dans un non-réutilisables façon).
Si vous modifiez votre code pour utiliser shared_ptr au lieu de cela, vous avez supprimé la nécessité de définir un copier/sémantique de propriété explicitement (et à définir le constructeur de copie et l'affectation dans votre pimpl de base). Vous pourrez également utiliser le code qui est déjà défini et testé (puisqu'il fait partie de la bibliothèque).
Parce que shared_ptr est disponible et déjà implémente les fonctionnalités et tous les "gotcha"s.
OriginalL'auteur utnapistim
Moi aussi, je me demande si c'est approprié comme une alternative de pointeur intelligent.
Mais, de l'OMI, à être un pointeur intelligent, une classe doit être utilisable comme un pointeur c'est à dire :
Donc, oui, il une sorte de gestion de la mémoire, mais ce n'est pas un pointeur intelligent, car il n'a pas la sémantique d'un pointeur.
Comme mentionné dans les commentaires, le pimpl idiome est vraiment très utile pour le maintien de la compatibilité et il peut aussi favoriser le développement que vous n'avez pas à recompiler le contenant de la classe.
MAIS avoir le dernier avantage, vous ne devez pas définir à l'intérieur de la classe (c'est à dire des Données) à l'intérieur de la classe parent, mais plutôt de mettre juste une déclaration de l'avant et mettre de la définition réelle à l'intérieur d'un autre en-tête.
Et, je trouve pas ça utile pour le développement futur de déclarer variante de Données à l'intérieur de la classe Fred, parce que si vous avez besoin d'ajouter une autre classe, vous aurez besoin de modifier Fred au lieu de simplement créer une autre classe. Cela pourrait être voulu, mais je vous suggère d'éviter que la partie.
Si je n'étais pas clair à propos de quelque chose, n'hésitez pas à poser des questions!
OriginalL'auteur fstamour
Le C++ FAQ la réponse semble plus être un exemple simpliste comment gérer les données partagées (à l'aide de la copie sur écriture). Il y a plusieurs aspects manquants, qui peut être important.
N/A à mon avis, pour 1.
Pour éviter la surcharge introduite avec "externes" de comptage de référence comme avec
std::shared_ptr
vous pouvez utiliser un intrusive ref mécanisme de comptage comme décrit dans Andrei Alexandrescu de l' livre Modern C++ Design. Le Loki::COMRefCounted classe montre comment mettre en œuvre une telle politique sur la propriété pour Windows partagé des objets COM.Essentiellement, il se résume à la smart pointeur de la classe de modèle d'accepter une interface qui gère le comptage de référence et le chèque de
delete
disponibilité dans le pointee instance de la classe elle-même. Je ne sais pas si le STD C++ de la bibliothèque prend en charge de mettre en œuvre de telles politiques de remplacement pour lestd::shared_ptr
classe.Nous sommes à l'aide de la Loki Bibliothèque uniquement pour la smart pointeur de modèle dans un certain nombre de projets intégrés très réussie. Surtout à cause de cette caractéristique du modèle granularité fine des aspects de l'efficacité.
Note que le projet (builtin) les implémentations ne sont pas thread-safe par défaut.
Si tout cela ci-dessus aspects ne concernent pas votre but, je me propose d'aller pour la simple
std::shared_ptr
représentation de votreFred::Data
classe, comme indiqué dans la sellibitze de réponse. Je suis également d'accord avec les points qu'il a fait dans le dernier alinéa, la référence de comptage et de pointeur intelligent sémantique est sujette à obtenir mal compris et mal mis en œuvre.Si le C++11 standard ou boost ne sont pas les options d'aller pour vous, le loki bibliothèque fournit toujours facile à intégrer et robuste smart pointeur de la mise en œuvre.
OriginalL'auteur πάντα ῥεῖ