《More Effective C++》技术篇——Reference counting(引用计数)

  • Reference counting这项技术,允许多个等值对象共享同一实值。此技术的发展有两种动机,第一是为了简化heap object周边的簿记工作。它可以消除“记录对象拥有权”的负荷,因为当对象运用了reference counting技术,它便拥有它自己。一旦不再有任何人使用它,它便会自动销毁自己。reference counting的第二个发展动机则只是为了实现一种常识。如果许多对象有相同的值,将那个值存储多次是件愚蠢的事。最好是让所有等值对象共享一份实值就好。

  • 以下是一个包含了Copy-On-Write技术的Reference counting基类,有兴趣建议去看看原书中的文章,作者一步步引导写出这个类,写的很详细,这里因为篇幅原因就不赘述。

// RCObject.h
// template class,用来产生smart pointers-to-T objects;T必须继承自RCObject 
template<class T>
class RCPtr {
public:
    RCPtr(T* realPtr = 0);
    RCPtr(const RCPtr& rhs);
    ~RCPtr();

    RCPtr& operator=(const RCPtr& rhs);

    T* operator->() const;
    T& operator*() const;

private:
    T *pointee;
    void init();
};

// base class,用于reference-counted objects
class RCObject {
public:
    void addReference();
    void removeReference();

    void markUnshareable();
    bool isShareable() const;

    bool isShared() const;

protected:
    RCObject();
    RCObject(const RCObject& rhs);
    RCObject& operator=(const RCObject& rhs);
    virtual ~RCObject() = 0;
    
private:
    int refCount;
    bool shareable;
};

// RCObject.cpp
RCObject::RCObject() : refCount(0), shareable(true) {

}

RCObject::RCObject(const RCObject& rhs) : refCount(0), shareable(true) {

}

RCObject& RCObject::operator=(const RCObject& rhs) {
    return *this;
}

RCObject::~RCObject () {

}

void RCObject::addReference() {
    ++refCount;
}

void RCObject::removeReference() {
    if (--refCount == 0) {
        delete this;
    }
}

void RCObject::markUnshareable() {
    shareable = false;
}

bool RCObject::isShareable() const {
    return shareable;
}

bool RCObject::isShared() const {
    return refCount > 1;
}

template<class T>
void RCPtr<T>::init () {
    if (pointee == 0)
        return;
    if (pointee->isShareable() == false) {
        pointee = new T(*pointee);
    }
    pointee->addReference();
}

template<class T>
RCPtr<T>::RCPtr(T* realPtr) : pointee(realPtr) {
    init();
}

template<class T>
RCPtr<T>::RCPtr(const RCPtr& rhs) : pointee(rhs.pointee) {
    init();
}

template<class T>
RCPtr<T>::~RCPtr() {
    if (pointee) {
        pointee->removeReference();
    }
}

template<class T>
RCPtr<T>& RCPtr<T>::operator=(const RCPtr& rhs){
    if (pointee != rhs.pointee) {
        if (pointee) {
            pointee->removeReference();
        }
        pointee = rhs.pointee;
        init();
    }
    return *this;
}

template<class T>
T* RCPtr<T>::operator->() const {
    return pointee;
}

template<class T>
T& RCPtr<T>::operator*() const {
    return *pointee;
}
  • 然后,定义一个简单的String类,在String中包含表示其实值的struct StringValue,其中StringValue继承自上面的RCObject,使其拥有引用计数的能力。String代码如下所示。
// String.h
// 应用性class,这是应用程序开发人员接触的层面
#include <string.h>
#include "RCObject.h"
class String {
public:
    String(const char *initValue = "");

    const char& operator[] (int index) const;
    char& operator[] (int index);

private:
    // 以下struct用以表现字符串实值
    struct StringValue : public RCObject {
        char *data;

        StringValue(const char *initValue);
        StringValue(const StringValue& rhs);
        void init(const char *initValue);
        ~StringValue();
    };
    RCPtr<StringValue> value;
};

// String.cpp
void String::StringValue::init(const char *initValue) {
    data = new char[strlen(initValue) + 1];
    strcpy(data, initValue);
}

String::StringValue::StringValue(const char *initValue) {
    init(initValue);
}

String::StringValue::StringValue(const StringValue& rhs) {
    init(rhs.data);
}

String::StringValue::~StringValue() {
    delete [] data;
}

String::String(const char *initValue) : value(new StringValue(initValue)) {

}

const char& String::operator[](int index) const {
    return value->data[index];
}

char& String::operator[](int index) {
    if (value->isShared()) {
        value = new StringValue(value->data);
    }
    value->markUnshareable();
    return value->data[index];
}
  • 再来写个main,简单验证下。
#include <String.h>
#include <iostream>
int main()
{
    String s1 = "hello";
    String s2 = s1;

    std::cout << "s1[3] = " << s1[3] << std::endl;
    std::cout << "s2[3] = " << s2[3] << std::endl;

    s2[3] = 'x';

    std::cout << "s1[3] = " << s1[3] << std::endl;
    std::cout << "s2[3] = " << s2[3] << std::endl;
}
// 输出
s1[3] = l
s2[3] = l
s1[3] = l
s2[3] = x
  • Reference counting是个优化技术,其使用前提是:对象常常共享实值。如果这个假设失败,reference counting反而会赔上更多内存,执行更多代码。从另一个角度看,如果你的对象确实有“共同实值”的倾向,reference counting应可同时节省你的时间和空间。
  • 简单说,以下是使用reference counting改善效率的最适当时机:
    • 相对多数的对象共享相对少量的实值(必要)。
    • 对象实值的产生或销毁成本很高,或是它们使用许多内存(非必要)。
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值