-->

在C ++的面向对象样式的引用计数(Reference Counting in C++ OO-Sty

2019-07-30 19:04发布

我来到防空火炮在一个有趣的实施基类的C ++常见问题 ,据我了解天真,可以作为一些智能指针实现的替代(如shared_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. 如果是适合的,你为什么认为它不是应用较多?

Answer 1:

这是一个合适的替代品,以用于管理对象的生命周期智能指针的方法,或只是自寻烦恼?

不,我不认为这是一个好主意,重塑在C ++ 11的引用计数特别是因为我们的std :: shared_ptr的现在。 您可以轻松实现你的可能是多态的引用计数PIMPL方法类的std :: shared_ptr的方面。 请注意,我们怎么不必须实现拷贝构造函数,赋值,析构函数不再和变异得到简单WRT引用计数和克隆:

// to be placed into a header file ...

#include <memory>
#include <utility>
#include <string>

class Fred
{
public:
    static Fred create1(std::string const& s, int i);
    static Fred create2(float x, float y);

    void sampleInspectorMethod() const;   // No changes to this object
    void sampleMutatorMethod();           // Change this object

private:
    class Data;
    std::shared_ptr<Data> data_;

    explicit Fred(std::shared_ptr<Data> d) : data_(std::move(d)) {}
};

...和...实施

// to be placed in the corresponding CPP file ...

#include <cassert>
#include "Fred.hpp"

using std::shared_ptr;

class Fred::Data
{
public:
    virtual ~Data() {}                               // A virtual destructor
    virtual shared_ptr<Data> clone() const = 0;      // A virtual constructor
    virtual void sampleInspectorMethod() const = 0;  // A pure virtual function
    virtual void sampleMutatorMethod() = 0;
};

namespace {

class Der1 : public Fred::Data
{
public:
    Der1(std::string const& s, int i);
    virtual void sampleInspectorMethod() const;
    virtual void sampleMutatorMethod();
    virtual shared_ptr<Data> clone() const;
    ...
};

// insert Der1 function definitions here

class Der2 : public Data
{
public:
    Der2(float x, float y);
    virtual void sampleInspectorMethod() const;
    virtual void sampleMutatorMethod();
    virtual shared_ptr<Data> clone() const;
    ...
};

// insert Der2 function definitions here

} // unnamed namespace

Fred Fred::create1(std::string const& s, int i)
{
    return Fred(std::make_shared<Der1>(s,i));
}

Fred Fred::create2(float x, float y)
{
    return Fred(std::make_shared<Der2>(x,y));
}

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_.unique()) data_ = data_->clone();
    assert(data_.unique());

    // Now we "pass the method through" to *data_:
    data_->sampleMutatorMethod();
}

(另)

如果是适合的,你为什么认为它不是应用较多?

我想引用计数,如果你自己实现它,是比较容易得到错误的。 它还具有可在多线程环境慢,因为引用计数器必须递增和递减原子的声誉。 但我想,由于C ++ 11提供的shared_ptr和移动语义,这个写入时复制模式可能会更受欢迎一点试。 如果启用了移动语义的弗雷德类,你可以避开一些的原子递增引用计数器的成本。 因此,从一个位置移动Fred对象到另一个应该是速度甚至比复制它。



Answer 2:

这是一个合适的替代品,以用于管理对象的生命周期智能指针的方法,或只是自寻烦恼?

这是一个选择,但除非你有一个很好的理由使用它,它只是重新发明轮子(在不可重复使用的方式)。

如果你改变你的代码使用的shared_ptr来代替,你会已经删除了需要明确定义复制/所有权语义(和你的平普尔基础来定义拷贝构造函数和赋值)。 您也将使用已经定义并测试代码(因为它是库的一部分)。

如果是适合的,你为什么认为它不是应用较多?

由于shared_ptr的可用和已经实现的功能和所有的“疑难杂症” S。



Answer 3:

我也一样,我不知道它是否适合作为智能指针替代。

但是,国际海事组织,是一个智能指针,类必须可被用作一个指针,即:

SmartPtr<int> ptr = new int(42);
int x = *ptr;

所以,是的,它成为一个排序内存管理,但由于它没有指针的语义它不是一个聪明的指针。

正如在评论中提到的,PIMPL方法是保持兼容性真正有用的,它也能促进发展,你不必重新编译包含类。 但有后发优势,就不能定义的内部类(即数据)父类的里面,而是只放了向前声明,并把实际定义另一头内。

class Fred {
    ...
private:

class Data;

};

而且,我发现它对于未来的发展没有什么用处申报数据的变型的Fred类里面,因为如果你需要添加另一个类,你将需要修改弗雷德,而不是仅仅创造另一个类的。 这可以想,但我建议你避免部分。

如果我是不清楚的东西,随便问的问题!



Answer 4:

  1. C ++的常见问题的回答似乎更是一个简单的例子,如何来管理共享数据(使用上写副本)。 有几个方面的缺失,这可能是很重要的。

  2. n,其中我对1的意见/ A。

为了避免与“外部”引用计数与引入的开销std::shared_ptr ,你可以使用一个侵入裁判计数机制中所描述安德烈Alexandrescu的的书现代C ++设计 。 在洛基:: COMRefCounted类展示了如何实现这种所有权政策的Windows共享COM对象。

本质上,它归结为智能指针模板类接受管理的引用计数和检查的接口delete的指针对象类实例本身的可用性。 我不知道,如果STD C ++库支持,以实现对这样的政策覆盖std::shared_ptr类。

我们使用的洛基库仅在一些嵌入式项目的智能指针模型非常成功地。 特别是由于该功能的效率的精细粒度方面的建模。

需要注意的是所提出的(内置)的实现不是线程默认情况下是安全的。

如果所有的上述方面并不关注你的目的,我建议去简单std::shared_ptr您的表示Fred::Data如图sellibitze的回答类。 我也同意他在最后一段拼成的点,引用计数和智能指针语义是容易得到它误解和错误的实现。

如果C ++ 11标准或升压不选择去你的,洛基库还提供了一个易于集成和强大的智能指针实现。



文章来源: Reference Counting in C++ OO-Style