前往小程序,Get更优阅读体验!
立即前往
发布
社区首页 >专栏 >C++ 引用计数技术简介(2/3)

C++ 引用计数技术简介(2/3)

作者头像
恋喵大鲤鱼
发布2022-11-29 21:29:43
发布2022-11-29 21:29:43
60000
代码可运行
举报
文章被收录于专栏:C/C++基础C/C++基础
运行总次数:0
代码可运行

文章目录

1.一个引用计数基类

Reference-counting 可用于字符串以外的场合,任何 class 如果其不同的对象可能拥有相同的值,都适用此技术。但是如果重写class以便适用reference counting可能需要大量的工作。

我们可以设计一个引用计数基类 RCObject,供想拥有引用计数的类继承。RCObject将“引用计数器”本身以及用以增减引用数值的函数封装起来。此外,还包括销毁对象值的函数,设置不可共享标的函数,返回共享标志的函数,查询是否在被共享的函数,查询引用计数的数目。没有必要提供一个设定共享标志位true的成员函数,因为所有的对象值在默认情况下都是可共享的。这里设定一旦某个对象被贴上"不可共享"标签,其永远都将是不可共享。

RCObject 定义如下:

代码语言:javascript
代码运行次数:0
复制
// 引用计数基类  
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;  
}  

注意: (1)RCObject 的赋值运算符 opeator=() 什么也没有做,实际上可共享的实值实际不太可能被赋值。例如在自定义 String 类中,实值 StringValue 并不不会被赋值,而是 String 对象的赋值。

(2)RCObject::removeReference 的责任不只在于将对象的 refCount 递减,而有当引用计数 refCount 为 0 时,销毁实值对象。使用delete this来销毁实值对象,那就要求 *this 是 heap 对象。

2.基于引用计数基类的 String

基于引用计数基类的 String 设计如下:

代码语言:javascript
代码运行次数:0
复制
class String {
private:
    Struct StringValue:public RCObject{
        char* data;
        
	    StringValue(const char* initValue);
	    ~StringValue();
    };
    StringValue* value;  

public:
    String(const char* initValue="");//constructor
    String(const String& rhs);//copy constructor
    String& operator=(const String& rhs); //assignment operator
    ~String(); //destructor
};

// StringValue 的构造函数。
String::StringValue::StringValue(const char* initValue):refCount(1){
     data=new char[strlen(initValue)+1];
     strcpy(data,initValue);
 }

// StringValue 的析构函数。
String::StringValue::~StringValue() {
    delete[] data;
}

这一版本的 StringValue 几乎与前一版本完全相同,唯一的改变是 StringValue 的 member functions 不再处理引用计数 refCount 字段,改由 RCObject 掌握。

3.自动操作引用次数

RCObject class 存放了引用次数,也给出了操作引用次数的 member fucntions,这些函数的调用动作还是得用户手动写到其他的 class内,并且通过 String constructor 和 String assignment operator 调用 StringValue 对象所提供的 addReference 和 removeReference。这里,我们使用可复用的类,不必让用户类去操作引用次数。这里可复用的类产生的对象,我们称之为 smart pointer。

下面使用 template 来实现 smart pointers,指向 reference-counted 实值对象。

代码语言:javascript
代码运行次数:0
复制
// 智能指针模板类,用来自动执行引用计数实值类成员的操控动作  
template<typename 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;  //dumb pointer
    void init(); //共同的初始化操作  
};  
// 共同的初始化操作
template<typename T>  
void RCPtr<T>::init(){  
    if (pointee == 0) return;  
    if (pointee->isShareable() == false) {  
        pointee = new T(*pointee);  
    }  
    pointee->addReference();  
}  
// 构造函数  
template<typename T>  
RCPtr<T>::RCPtr(T* realPtr) :pointee(realPtr){  
    init();  
}  
// 拷贝构造函数  
template<typename T>  
RCPtr<T>::RCPtr(const RCPtr& rhs) : pointee(rhs.pointee){  
    init();  
}  
// 析构函数  
template<typename T>  
RCPtr<T>::~RCPtr(){  
    if (pointee)  
        pointee->removeReference();  
}  
// 赋值运算符  
template<typename T>  
RCPtr<T>& RCPtr<T>::operator=(const RCPtr& rhs){  
    if (pointee != rhs.pointee) {  
        if (pointee)  
            pointee->removeReference();  
        pointee = rhs.pointee;  
        init();  
    }  
    return *this;  
}  
//重载成员选取运算符 -> 
template<typename T>  
T* RCPtr<T>::operator->() const { return pointee; }  
//重载解引用运算符*  
template<typename T>  
T& RCPtr<T>::operator*() const { return *pointee; }  

4.最终 String

在上面的基础之上,我们利用具有服用性质的RCObject和RCPtr classes为基础,建造一个reference-counted String class。每一个具有引用计数功能的 String 对象均以此数据结构实现出来:

最终的String描述如下:

代码语言:javascript
代码运行次数:0
复制
class String {                             
public:                                  
    String(const char *value = "");//构造函数  
    const char& operator[](int index) const;//重载[]运算符,针对const Strings  
    char& operator[](int index);//重载[]运算符,针对non-const Strings  
private:  
    struct StringValue : public RCObject {//继承自引用计数基类  
        char *data;  
        
        StringValue(const char *initValue);//构造函数  
        StringValue(const StringValue& rhs);//拷贝赋值运算符  
        void init(const char *initValue);  
        ~StringValue();//析构函数  
    };  
    RCPtr<StringValue> value;//智能指针对象  
};  

//String::StringValue实现代码  
void String::StringValue::init(const char *initValue){  
    data = new char[strlen(initValue) + 1];  
    strcpy(data, initValue);  
}  
//StringValue类的构造函数  
String::StringValue::StringValue(const char *initValue){  
    init(initValue);  
}  
//StringValue类的拷贝赋值运算符  
String::StringValue::StringValue(const StringValue& rhs){  
    init(rhs.data);  
}  
//StringValue类的析构函数  
String::StringValue::~StringValue(){  
    delete[] data;  
}  

//String实现代码  
//String类的构造函数  
String::String(const char *initValue): value(new StringValue(initValue)) {}  
//重载[]运算符,针对const Strings  
const char& String::operator[](int index) const{  
    return value->data[index];  
}  
//重载[]运算符,针对non-const Strings  
char& String::operator[](int index){  
    if (value->isShared()) {  
        value = new StringValue(value->data);  
    }  
    value->markUnshareable();  
    return value->data[index];  
}  

注意,这里使用智能指针对象的 String 并不需要显示定义 copy constructor 和 assignment operator,因为这些编译器为默认生成,并且会自动调用 String 内 RCPtr member的copy constructor 和 assignment operator,而后者又会自动执行对 StringValue对象的所有处理,包括引用次数。


参考文献

More Effective C++.Scott Meyers著,侯捷译.P183-213 more effective c++读书笔记

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2022-09-08,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 文章目录
  • 1.一个引用计数基类
  • 2.基于引用计数基类的 String
  • 3.自动操作引用次数
  • 4.最终 String
  • 参考文献
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档