C++如何使用简单的引用计数

来源:互联网 发布:匪萌十月网络班 编辑:程序博客网 时间:2024/05/05 01:32

若你需要的只是分发指向同一个对象的多个指针,并且当最后一个指针消失的时候自动释放该对象的能力的话,你可以使用以下的类似“智能指针”的类。

代码如下:

// Fred.hclass FredPtr;class Fred {public:    Fred() : count_(0) { } // 所有的构造函数都要设置count_为0!    private:    friend FredPtr;        // 友元类    unsigned count_;    // count_必须被所有构造函数初始化    // count_就是指向this的FredPtr对象数目};class FredPtr {public:    Fred* operator-> () { return p_; }    Fred& operator*  () { return *p_; }    FredPtr(Fred* p)    : p_(p) { ++p_->count; }    // p不能为NULL    ~FredPtr()          { if (--p_->count_ == 0) delete p_; }    FredPtr(const FredPtr& p) : p_(p.p_) { ++p_->count_; }    FredPtr& operator= (const FredPtr& p) {        // 不要改变这些语句的顺序        // 如此的顺序适当地处理了(自赋值)        ++p.p_->count;        if (--p_->count_ == 0) deleete p_;        p_ = p.p_;        return *this;    }    private:    Fred* p_;       // p_永远不为NULL};

当然,你也可以使用嵌套类,将FredPtr改为Fred::Ptr


引用计数可以由指针计数或引用计数完成,上方显示了如何使用指针语义进行引用计数,下方将展示如何使用引用语义进行引用计数。

基本思想是允许用户认为他们在复制Fred对象,而实际上真正的实现并不真正复制,直到一些用户试图修改隐含的Fred对象时才复制。

Fred::Data类装载了Fred类所有的数据。Fred::Data也有一个额外的成员count_,来管理引用计数。Fred类最后成了一个指向Fred::Data的“智能指针”(内部的)

class Fred {public:    Fred();                 // 默认构造函数    Fred(int i, int j);     // 普通构造函数    Fred(const Fred& f);    // 拷贝构造函数    ~Fred();        void sampleInspectorMethod() const; // this对象不会变    void sampleMutatorMethod();         // 会改变this()对象        //...private:    class Data {    public:        Data();        Data(int i, int j);        Data(const Data& d);                // 由于只有Fred能访问Fred::Data对象        // 只要你愿意,你可以使得Fred::Data的数据为public        // 但如果那样使你不爽,就把数据作为private        // 还要用friend Fred,使Fred成为友元类                unsigned count_;        // count_是指向this的Fred对象的数目        // count_必须被所有的构造函数初始化为1        // (从1开始是因为它被创建它的Fred对象所指)    };        Data* data_;        Fred::Data::Data()              : count_(1) {}    Fred::Data::Data(int i, int j)  : count_(1) {}    Fred::Data::Data(const Data& d) : count_(1) {}        Fred::Fred()                    : data_(new Data()) {}    Fred::Fred(int i, int j)        : data_(new Data(i, j)) {}        Fred::Fred(const Fred& f)        : data_(f.data_) {        ++data_->count_;    }        Fred& Fred::operator= (const Fred& f) {        // 不要更改这些语句的顺序        // 如此的顺序恰当的处理了自赋值问题        ++f.data_->count_;        if (--data_->count_ == 0) delete data_;        data_ = f.data_;        return *this;    }        Fred::~Fred() {        if (--data_->count_ == 0) delete data_;    }        void Fred::sampleInspectorMethod() const {        // 该方法承诺"const",不改变*data_中任何东西        // 除此之外,任何数据访问将简单地使用data_->..    }        void Fred::sampleMutatorMethod() {        // 该方法可能需要改变*data_中的数据        // 因此首先检查this是否唯一指向*data_        if (data_->count_ > 1) {            Data* d = new Data(*data);            -- data_->count_;            data_ = d;        }        assert(data_->count_ == 1);                // 现在改方法如常进行"data_->..."访问    }};

如果非常经常地调用 Fred 的默认构造函数,你可以为所有通过Fred::Fred()构造的Fred 共享一个公共的Fred::Data 对象来消除那些 new调用。为避免静态初始化顺序问题,该共享的 Fred::Data 对象在一个函数内“首次使用”时才创建。如下就是对以上的代码做的改变(注意,该共享的Fred::Data对象的析构函数永远不会被调用;如果这成问题的话,要么解决静态初始化顺序的问题,要么索性返回到如上描述的方法):

// 如果非常经常地调用 Fred 的默认构造函数,// 你可以为所有通过Fred::Fred()构造的Fred 共享一个公共的Fred::Data 对象来消除那些 new调用。// 为避免静态初始化顺序问题,该共享的 Fred::Data 对象在一个函数内“首次使用”时才创建。// 如下就是对以上的代码做的改变(注意,该共享的Fred::Data对象的析构函数永远不会被调用;// 如果这成问题的话,要么解决静态初始化顺序的问题,要么索性返回到如上描述的方法):class Fred {public:    //..private:    //..    static Data* defaultData();};Fred::Fred()    : data_(defaultData()) {    ++ data_->count_;}Fred::Data* Fred::defaultData() {    static Data*p = NULL;    if (p == NULL) {        p = new Data();        ++ p->count_;       // 确保它不为0    }    return p;}


// 前一个FAQ给出了引用语义的引用计数策略,// 但迄今为止都针对单个类而不是分层次的类。// 本FAQ扩展之前的技术以允许为类层次提供引用计数。// 基本不同之处在于现在Fred::Data是类层次的根,着可能使得它有一些虚函数。// 注意 Fred 类本身仍然没有任何的虚函数。// 虚构造函数用法用来建立 Fred::Data 对象的拷贝。要选择创建哪个派生类,// 如下的示例代码使用了命名构造函数用法,但还有其它技术(构造函数中加一个switch语句等)。// 示例代码假设了两个派生类:Der1和Der2。派生类的方法并不查觉引用计数。class Fred {public:    static Fred create1(const std::string& s, int i);    static Fred create2(float x, float y);        Fred(const Fred& f);    Fred& operator= (const Fred& f);    ~Fred();        void sampleInspectorMethod() const; // this对象不会被改变    void sampleMutatorMethod();         // 会改变this对象    private:        class Data {    public:        Data()              : count_(1) {}        Data(const Data& d) : count_(1) {}                  // 不要拷贝'count_'成员        Data& operator= (const Data&) { return *this; }     // 不要拷贝'count_'成员        virtual ~Data()               { assert(count_== 0); }   // 虚析构函数        virtual Data* clone() const = 0;                        // 虚构造函数        virtual void sampleInspectorMethod() const = 0;         // 纯虚函数        virtual void sampleMutatorMethod() = 0;        private:        unsigned count_;    // count_不需要是protected的        friend Fred;        // 允许Fred访问count_    };        class Der1 : public Data {    public:        Der1(const std::string& 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);    // 创建一个拥有*data的Fred智能引用    // 它是private的以迫使用户使用createXXX()方法    // 要求:data必能为NULL        Data* data_;        // invariant: data_ is never NULL};Fred::Fred(Data* data) : data_(data) { assert(data != NULL); }Fred Fred::create1(const std::string& 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(const Fred& f)    : data_(f.data_) {    ++ data_->count_;}Fred& Fred::operator= (const Fred& f) {    // 不要改变这些语句的顺序!    // 如此的顺序适当地处理了自赋值问题    ++ f.data_->count_;    if (--data->count_ == 0) delete data_;    data_ = f.data_;    return *this;}Fred::~Fred() {    if (--data_->count_ == 0) delete data_;}void Fred::sampleInspectorMethod() const {    // 该方法承诺"const", 不改变*data_中的任何东西    // 因此我们只要直接把方法传递给*data_    data_->sampleInspectorMethod();}void Fred::sampleMutatorMethod() {    // 该方法可能需要改变*data_中的数据    // 因此首先检查this是否唯一指向*data_    if (data_->count_ > 1) {        Data* d = data_->clone();   // 虚构造函数用法        -- data_->count_;        data_ = d;    }    assert(data_->count_ == 1);        // 现在直接把方法传递给*data_;    data_->sampleInspectorMethod();}// 自然,Fred::Der1和Fred::Der2的构造函数和sampleXXX方法将需要被以某周途径适当的实现



原创粉丝点击