C ++ OO风格中的引用计数 [英] Reference Counting in C++ OO-Style

查看:190
本文介绍了C ++ OO风格中的引用计数的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

C ++常见问题解答

I came accross an intriguing implementation of a base class on the C++ FAQ that, according to my naive understanding, could serve as an alternative to some of the smart pointer implementations (e.g. shared_ptr). Here's the example code verbatim, but please follow the link above for an explanation:

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 ++库中使用这种方法;虽然看起来很优雅。假设单线程环境,为了简单起见,请回答以下问题:

I don't see this approach being used in any C++ libraries; although it seems quite elegant. Assuming a single-threaded environment, for the sake of simplicity, please answer the following questions:


  1. 这是智能指针的合适替代方案

  2. 如果它是合适的,你为什么认为它不会更频繁地使用?


推荐答案


这是管理对象生命周期的智能指针方法的合适替代方案,

Is this a suitable alternative to the smart pointer approach for managing the lifetime of objects, or is it just asking for trouble?

不,我不认为重新编写引用计数是个好主意,特别是因为我们有std :: shared_ptr现在在C ++ 11。你可以很容易地实现你可能的多态性引用计数Pimpl成语类std :: shared_ptr。注意我们不需要实现copy ctor,assignment,dtor和变异更简单w.r.t.参考计数器和克隆:

No, I don't think it's a good idea to reinvent reference counting especially since we have std::shared_ptr now in C++11. You can easily implement your possibly polymorphic reference-counted Pimpl idiom class in terms of std::shared_ptr. Notice how we don't have to implement copy ctor, assignment, dtor anymore and mutation gets simpler w.r.t. the reference counter and cloning:

// 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)) {}
};

...和实施...

...and the implementation...

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

(未测试)

如果它是合适的,为什么你认为它不会更频繁地使用?

If it is suitable, why do you suppose it's not used more often?

计数,如果你自己实现,更容易出错。它也有在多线程环境中缓慢的声誉,因为引用计数器必须以原子方式递增和递减。但我猜想由于C ++ 11提供shared_ptr和移动语义,这个写时复制模式可能会再次更受欢迎。如果为Fred类启用移动语义,则可以避免原子地增加引用计数器的一些成本。所以将Fred对象从一个位置移动到另一个位置应该比复制它更快。

I think reference counting, if you implement it yourself, is easier to get wrong. It also has the reputation of being slow in multithreaded environments because the reference counters have to be incremented and decremented atomically. But I guess due to C++11 which offers shared_ptr and move semantics, this copy-on-write pattern might get a bit more popular again. If you enable move semantics for the Fred class you can avoid some of the costs of atomically incrementing reference counters. So moving a Fred object from one location to another should be even faster than copying it.

这篇关于C ++ OO风格中的引用计数的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆