专栏首页C/C++基础C++引用计数(reference counting)技术简介(3)

C++引用计数(reference counting)技术简介(3)

1.将Reference Counting加到既有的Class

要想将引用计数施加到现有的实值对象Widget上,按照前面讨论的,都需要修改Winget类的源代码。但是,有时程序库的内容不是我们呢可以修改的,又该如何做呢?

如果令Widget继承自RCObject,我们必须增加一个RCWidget class给用户使用,这很像之前关于String/StringValue的讨论。RCWidget扮演String的角色,Widget扮演StringValue的角色。整个设计结构如下:

但这么做的话就需要修改Widget使其继承自RCObject。我们可以增加一个新的CountHolder class,用以持有引用计数,并令CountHolder继承自RCObject。我们也令CountHolder内含一个指针,指向一个Widget。然后将smart RCPtr template以同样聪明的RCIPtr template取代,RCIPtr template包含CountHolder的一个指针。RCIPtr 的”I”意指”indirect”间接。修改后的设计如下:

引用计数基类RCObject基本不变,其源码如下:

//引用计数基类  
class RCObject{  
public:  
    RCObject();//构造函数  
    RCObject(const RCObject& rhs);//拷贝构造函数  
    RCObject& operator=(const RCObject& rhs);//拷贝赋值运算符  
    virtual ~RCObject() = 0;//析构函数

    void addReference();//增加引用计数  
    void removeReference();//减少引用计数,如果变为0,销毁对象  
    void markUnshareable();//将可共享标志设为false  
    bool isShareable() const;//判断其值是否可共享  
    bool isShared() const;//判断其值是否正在被共享  
    int getRefCount();//返回引用计数    
private:  
    int refCount;//保存引用计数  
    bool shareable;//保存其值是否可共享的状态  
};  

//构造函数,这里refCount设为0,让对象创建者自行或将refCoun设为1  
RCObject::RCObject(void) :refCount(0), shareable(true){}  

//拷贝构造函数,总是将refCount设为0,因为正在产生一个新对象,只被创建者引用  
RCObject::RCObject(const RCObject&) : refCount(0), shareable(true){}  

//拷贝赋值运算符,这里只返回*this,因为左右两方RCObject对象的外围对象个数不受影响  
RCObject& RCObject::operator=(const RCObject& rhs){  
    return *this;  
}  

//析构函数  
RCObject::~RCObject(){}  

//增加引用计数  
void RCObject::addReference(){  
    ++refCount;  
}  

//减少引用计数,如果变为0,销毁对象  
void RCObject::removeReference(){  
    if (--refCount == 0)  
        delete this;  
}  

//将追踪其值是否可共享的成员设为false  
void RCObject::markUnshareable(){  
    shareable = false;  
}  

//判断其值是否可共享  
bool RCObject::isShareable() const{  
    return shareable;  
} 

//判断其值是否正在被共享  
bool RCObject::isShared() const{  
    return refCount>1;  
}  

//返回引用计数  
int RCObject::getRefCount(){  
    return refCount;  
}  

template RCIPtr的实现如下:

//智能指针模板类,用来自动执行引用计数类成员的操控动作  
template<typename T>  
class RCIPtr{  
public:  
    RCIPtr(T* realPtr = 0);//构造函数  
    RCIPtr(const RCIPtr& rhs);//拷贝构造函数  
    ~RCIPtr();//析构函数  
    RCIPtr& operator=(const RCIPtr& rhs);//拷贝赋值运算符  
    const T* operator->() const;//重载->运算符  
    T* operator->();//重载->运算符  
    const T& operator*() const;//重载*运算符  
    T& operator*();//重载*运算符  
private:  
    struct CountHolder :public RCObject{  
        ~CountHolder() { delete pointee; }  
        T* pointee;  
    };  
    CountHolder* counter;  
    void init();//初始化操作  
    void makeCopy();//copy-on-write中的copy部分  
};  
//共同的初始化操作  
template <typename T>  
void RCIPtr<T>::init(){  
    if (counter->isShareable() == false){  
        T* oldValue = counter->pointee;  
        counter = new CountHolder;  
        counter->pointee = new T(*oldValue);  
    }  
    counter->addReference();  
}  
//构造函数  
template <typename T>  
RCIPtr<T>::RCIPtr(T* realPtr) :counter(new CountHolder){  
    counter->pointee = realPtr;  
    init();  
}  
//拷贝构造函数  
template <typename T>  
RCIPtr<T>::RCIPtr(const RCIPtr& rhs) :counter(rhs.counter){  
    init();  
}  
//析构函数  
template <typename T>  
RCIPtr<T>::~RCIPtr(){  
    counter->removeReference();  
}  
//拷贝赋值运算符  
template <typename T>  
RCIPtr<T>& RCIPtr<T>::operator=(const RCIPtr& rhs){  
    if (counter != rhs.counter){  
        counter->removeReference();  
        counter = rhs.counter;  
        init();  
    }  
    return *this;  
}  
//重载->运算符,const版本  
template<typename T>  
const T* RCIPtr<T>::operator->() const { return counter->pointee; }  
//重载*运算符,non-const版本  
template<typename T>  
const T& RCIPtr<T>::operator*() const { return *(counter->pointee); }  
//copy-on-write中的copy部分  
template <typename T>  
void RCIPtr<T>::makeCopy(){  
    if (counter->isShared()){  
        T* oldValue = counter->pointee;  
        counter->removeReference();  
        counter = new CountHolder;  
        counter->pointee = new T(*oldValue);  
        counter->addReference();  
    }  
}  
//重载->运算符,non-const版本  
template <typename T>  
T* RCIPtr<T>::operator->(){  
    makeCopy();  
    return counter->pointee;  
}  
//重载*运算符,non-const版本  
template <typename T>  
T& RCIPtr<T>::operator*(){  
    makeCopy();  
    return *(counter->pointee);  
}  

RCIPtr和RCPtr之间有两个差异,一个是RCPtr对象直接指向实值,而RCIPtr对象通过中介层“CountHolder对象”指向实值。第二是RCIPtr将operator->和operator*重载了,如此一来只要有non-const access发生在被指物身上,copy-on-write(写时复制)就会被执行。

有了RCIPtr,RCWidget的实现就很容易,因为RCWidget的每一个函数都只是通过底层的RCIPtr转调用对应的Widget函数。Widget和RCWidget的示例代码如下。

class Widget{  
public:  
    Widget(int s = 0) :size(s){}  
    Widget(const Widget& rhs) { size = rhs.size; }  
    ~Widget(void) {}  

    Widget& operator=(const Widget& rhs){  
        if (this == &rhs)  
            return *this;  
        this->size = rhs.size;  
        return *this;  
    }  
    void doThis() { std::cout << "doThis()" << std::endl; }  
    int showThat() const {   
        std::cout << "showThat()" << std::endl;   
        return size;   
    }  
private:  
    int size;  
};  

class RCWidget{  
public:  
    RCWidget(int size = 0) :value(new Widget(size)){}  
    ~RCWidget() {}  

    void doThis() { value->doThis(); }  
    int showThat() const { return value->showThat(); }  
private:  
    RCIPtr<Widget> value;  
};  

注意,RCWidget没有申明拷贝构造函数,也没有赋值运算符和析构函数,就像先前的String class一样,不再需要撰写这些函数了,因为编译器生成的默认版本做了正确的事情。

2.总结

引用计数的实现需要成本。每一个拥有计数能力的实值都有一个引用计数器,而大部分操作都需要能够以某种方式检查或处理这个引用计数器,因此对象的实值需要更多内存。而且引用计数的底层源代码比没有引用计数的复杂的多。

引用计数是个优化计数,其适用前提是对象常常共享实值。使用引用计数改善效率的时机有以下两个: 第一,相对多数的对象共享相对少量的实值; 第二,对象实值的产生或销毁成本很高,或是它们使用许多内存。


参考文献

[1]More Effective C++.Scott Meyers著,侯捷译.P183-213. [2]http://blog.csdn.net/ruan875417/article/details/48267527.

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • Linux命令(31)——find命令

    find命令用于在指定目录查找文件,可以指定一些匹配条件,如按文件名、文件类型、用户甚至是时间戳来查找文件。

    Dabelv
  • Linux 命令(123)—— iostat 命令

    iostat(IO statistics)命令被用于监视 CPU 和输入输出设备的使用情况。iostat 有一个弱点,它不能对某个进程进行深入分析,仅对系统的整...

    Dabelv
  • C/C++ const

    const是C语言的关键字,经C++扩充,功能变得强大,用法复杂。const用于定义一个常变量(只读变量)。当const与指针、引用、函数等结合起来使用时,情况...

    Dabelv
  • WPF 在绑定表达式添加计算

    很多时候一些简单的使用绑定需要对绑定的源做处理就需要通过转换器,这样的代码写起来不好看 本文告诉大家通过一个简单的库可以实现在界面绑定的时候通过表达式不需要转换

    林德熙
  • 你离真正的网络安全只差一步——“零信任”

    网络最初设计的目的是通过一个固定的边界来创建与外部世界相隔离的内部网络。内部网络被认为是可信赖的,而外部网络被认为是敌对的。目前,这些仍然是大多数网络专业人士的...

    SDNLAB
  • 推荐系统之矩阵分解模型

    最近在整理Embedding技术在推荐系统中的应用,总结了获取各类item2vec的方法,推荐系统中的矩阵分解作为解决item2vec问题初期技术方法之一,虽已...

    流川枫
  • 一篇文章让你看懂数据分析的目的、方法、工具及实际应用

      我特别不喜欢装逼的产品经理,看文章也一样不喜欢华而不实的。所以督促自己写文章时,把懂的、经历过的能细就写的尽量详细;不懂的就去学,然后把整理的笔记分享出来,...

    小莹莹
  • 一篇文章让你看懂数据分析的目的、方法、工具及实际应用

    我特别不喜欢装逼的产品经理,看文章也一样不喜欢华而不实的。所以督促自己写文章时,把懂的、经历过的能细就写的尽量详细;不懂的就去学,然后把整理的笔记分享出来,数据...

    华章科技
  • 突破Java面试(15)-分布式搜索引擎Elastic Search的工作流程

    ES无非就是写/查数据,你如果不明白你发起写入/搜索请求后,ES做了什么,那你该劝退了.

    JavaEdge
  • 矩阵乘法的Strassen算法+动态规划算法(矩阵链相乘和硬币问题)

    矩阵乘法的Strassen ---- 这个算法就是在矩阵乘法中采用分治法,能够有效的提高算法的效率。 先来看看咱们在高等代数中学的普通矩阵的乘法 ? 两个矩阵相...

    张俊怡

扫码关注云+社区

领取腾讯云代金券