草庐IT

c++ - C++ OO 风格的引用计数

coder 2023-11-13 原文

我在 C++ FAQ 上遇到了一个有趣的基类实现根据我天真的理解,它可以作为某些智能指针实现(例如 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. 如果合适,您认为为什么不经常使用它?

最佳答案

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

不,我不认为重新发明引用计数是个好主意,尤其是因为我们现在在 C++11 中有了 std::shared_ptr。您可以根据 std::shared_ptr 轻松实现您可能的多态引用计数 Pimpl 习语类。请注意我们如何不必再实现复制构造器、赋值、dtor,并且变异变得更简单 w.r.t.引用计数器和克隆:

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

(未经测试)

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

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

关于c++ - C++ OO 风格的引用计数,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/11788187/

有关c++ - C++ OO 风格的引用计数的更多相关文章

  1. ruby-on-rails - 如何优雅地重启 thin + nginx? - 2

    我的瘦服务器配置了nginx,我的ROR应用程序正在它们上运行。在我发布代码更新时运行thinrestart会给我的应用程序带来一些停机时间。我试图弄清楚如何优雅地重启正在运行的Thin实例,但找不到好的解决方案。有没有人能做到这一点? 最佳答案 #Restartjustthethinserverdescribedbythatconfigsudothin-C/etc/thin/mysite.ymlrestartNginx将继续运行并代理请求。如果您将Nginx设置为使用多个上游服务器,例如server{listen80;server

  2. ruby - 模块嵌套代码风格偏好 - 2

    我的假设是moduleAmoduleBendend和moduleA::Bend是一样的。我能够从thisblog找到解决方案,thisSOthread和andthisSOthread.为什么以及什么时候应该更喜欢紧凑语法A::B而不是另一个,因为它显然有一个缺点?我有一种直觉,它可能与性能有关,因为在更多命名空间中查找常量需要更多计算。但是我无法通过对普通类进行基准测试来验证这一点。 最佳答案 这两种写作方法经常被混淆。首先要说的是,据我所知,没有可衡量的性能差异。(在下面的书面示例中不断查找)最明显的区别,可能也是最著名的,是你的

  3. ruby - 一个 YAML 对象可以引用另一个吗? - 2

    我想让一个yaml对象引用另一个,如下所示:intro:"Hello,dearuser."registration:$introThanksforregistering!new_message:$introYouhaveanewmessage!上面的语法只是它如何工作的一个例子(这也是它在thiscpanmodule中的工作方式。)我正在使用标准的ruby​​yaml解析器。这可能吗? 最佳答案 一些yaml对象确实引用了其他对象:irb>require'yaml'#=>trueirb>str="hello"#=>"hello"ir

  4. ruby-on-rails - Ruby on Rails 计数器缓存错误 - 2

    尝试在我的RoR应用程序中实现计数器缓存列时出现错误Unknownkey(s):counter_cache。我在这个问题中实现了模型关联:Modelassociationquestion这是我的迁移:classAddVideoVotesCountToVideos0Video.reset_column_informationVideo.find(:all).eachdo|p|p.update_attributes:videos_votes_count,p.video_votes.lengthendenddefself.downremove_column:videos,:video_vot

  5. ruby - 使用多个数组创建计数 - 2

    我正在尝试按0-9和a-z的顺序创建数字和字母列表。我有一组值value_array=['0','1','2','3','4','5','6','7','8','9','a','b','光盘','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','','u','v','w','x','y','z']和一个组合列表的数组,按顺序,这些数字可以产生x个字符,比方说三个list_array=[]和一个当前字母和数字组合的数组(在将它插入列表数组之前我会把它变成一个字符串,]current_combo['0','0','0']

  6. ruby - 使用 `+=` 和 `send` 方法 - 2

    如何将send与+=一起使用?a=20;a.send"+=",10undefinedmethod`+='for20:Fixnuma=20;a+=10=>30 最佳答案 恐怕你不能。+=不是方法,而是语法糖。参见http://www.ruby-doc.org/docs/ProgrammingRuby/html/tut_expressions.html它说Incommonwithmanyotherlanguages,Rubyhasasyntacticshortcut:a=a+2maybewrittenasa+=2.你能做的最好的事情是:

  7. ruby - 如何计算 Liquid 中的变量 +1 - 2

    我对如何计算通过{%assignvar=0%}赋值的变量加一完全感到困惑。这应该是最简单的任务。到目前为止,这是我尝试过的:{%assignamount=0%}{%forvariantinproduct.variants%}{%assignamount=amount+1%}{%endfor%}Amount:{{amount}}结果总是0。也许我忽略了一些明显的东西。也许有更好的方法。我想要存档的只是获取运行的迭代次数。 最佳答案 因为{{incrementamount}}将输出您的变量值并且不会影响{%assign%}定义的变量,我

  8. c - Ruby - 源代码 - 编码风格 - 2

    查看Ruby代码,它具有以下proc_arity:staticVALUEproc_arity(VALUEself){intarity=rb_proc_arity(self);returnINT2FIX(arity);}更多的是C编码风格问题,但为什么staticVALUE在单独的一行而不是像这样的:staticVALUEproc_arity(VALUEself) 最佳答案 它来自UNIX世界,因为它有助于轻松grep函数的定义:$grep-n'^proc_arity'*.c或使用vim:/^proc_arity

  9. ruby - Chef LW 资源属性默认值如何引用另一个属性? - 2

    我正在尝试将一个资源属性的默认值设置为另一个属性的值。我正在为我正在构建的tomcat说明书定义一个资源,其中包含以下定义。我想要可以独立设置的“名称”和“服务名称”属性。当未设置服务名称时,我希望它默认为为“名称”提供的任何内容。以下不符合我的预期:attribute:name,:kind_of=>String,:required=>true,:name_attribute=>trueattribute:service_name,:kind_of=>String,:default=>:name注意第二行末尾的“:default=>:name”。当我在Recipe的新block中引用我

  10. arrays - Ruby 数组 += vs 推送 - 2

    我有一个数组数组,想将元素附加到子数组。+=做我想做的,但我想了解为什么push不做。我期望的行为(并与+=一起工作):b=Array.new(3,[])b[0]+=["apple"]b[1]+=["orange"]b[2]+=["frog"]b=>[["苹果"],["橙子"],["Frog"]]通过推送,我将推送的元素附加到每个子数组(为什么?):a=Array.new(3,[])a[0].push("apple")a[1].push("orange")a[2].push("frog")a=>[[“苹果”、“橙子”、“Frog”]、[“苹果”、“橙子”、“Frog”]、[“苹果”、“

随机推荐