Подсчет ссылок в стиле C++ OO -

Я наткнулся на интригующую реализацию базового класса в C++ FAQ , которая, согласно моему наивному мнению, может служить альтернативой некоторым реализациям интеллектуальных указателей (, например. общий _ptr ). Вот пример кода дословно,но, пожалуйста, перейдите по ссылке выше для объяснения:

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();
}

Я не вижу, чтобы этот подход использовался в каких-либо библиотеках C++; хотя выглядит довольно элегантно. Предполагая однопотоковую среду -, для простоты ответьте на следующие вопросы:

  1. Является ли это подходящей альтернативой подходу с использованием интеллектуальных указателей для управления временем жизни объектов, или это просто напрашивается на неприятности?
  2. Если он подходит, почему, по-вашему, его не используют чаще?

10
задан Verax 3 August 2012 в 01:59
поделиться