Ver*_*rax 10 c++ reference-counting
我在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();
}
Run Code Online (Sandbox Code Playgroud)
我没有看到这种方法在任何C++库中使用; 虽然看起来很优雅.假设单线程环境,为简单起见,请回答以下问题:
这是用于管理对象生命周期的智能指针方法的合适替代方案,还是仅仅是在寻找麻烦?
不,我认为重新引用引用计数并不是一个好主意,特别是因为我们现在在C++ 11中使用了std :: shared_ptr.您可以根据std :: shared_ptr轻松实现可能的多态引用计数Pimpl习语类.注意我们不再需要实现复制ctor,赋值,dtor,并且使用参考计数器和克隆变异变得更简单:
// 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)) {}
};
Run Code Online (Sandbox Code Playgroud)
......以及实施......
// 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();
}
Run Code Online (Sandbox Code Playgroud)
(另)
如果它合适,为什么你认为它不经常使用?
我认为引用计数,如果你自己实现,更容易出错.它还具有在多线程环境中速度慢的声誉,因为引用计数器必须以原子方式递增和递减.但我想由于C++ 11提供了shared_ptr和移动语义,这种写时复制模式可能会再次受欢迎.如果为Fred类启用移动语义,则可以避免以原子方式递增引用计数器的一些成本.因此将Fred对象从一个位置移动到另一个位置应该比复制它更快.