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

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

代码如下:

// Fred.h

class 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方法将需要被以某周途径适当的实现



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值
>