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:

  1. 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?
  2. 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!

OriginalL'auteur Verax | 2012-08-03