定义行为像指针的类(类比于智能指针)和定义行为像值的类
一直纠结于智能指针shared_ptr的引用计数如何实现。此前看C++ primer不太注意,今天再次翻到这一知识点,再细看一遍,受益非浅呀。原来引用计数的实现也不过如此。
1、定义行为像指针的类
这里为避免同名,且只强调引用计数的实现,自定义了一个HasPtr类,其中最大特点是引入引用计数器。
类指针的类,最大特点是底层数据共享。只对指针进行了浅拷贝。
引用计数的工作方式如下四大特点:
i、除了初始化对象外,每个构造函数(拷贝构造函数除外)还要创建一个引用计数来记录有多少对象与正在创建的对象共享状态。当创建一个对象时,只有一个对象共享状态,因此将计数器初始化为1。(初始化对象时,计数器计为1)
ii、拷贝构造函数不分配新的计数器,而是拷贝给定对象的数据成员,包括计数器。拷贝构造函数递增共享的计数器,指出给定对象的状态又被一个新用户所共享。(拷贝时,递增相应引用计数器)
iii、析构函数递减计数器,指出共享状态的用户少了一个。直至计数器变为0,则析构函数释放状态。(只有计数器为0时,才能释放共享内存)
iv、拷贝赋值运算符递增右侧运算对象的计数器。递减左侧运算对象赋值之前对象所持有的引用计数器。如果左侧运算对象的计数器变为0,意味着其共享状态没有用户了,拷贝赋值运算符就必须销毁状态。(右增左减相对应计数)
为此,引用计数器保存在动态内存中。当创建一个对象时,分配一个新的计数器。当拷贝或幅值对象时,拷贝指向计数器的指针。
具体实现,如下代码所示(只关注了引用计数器实现部分哈):
#include<string>
using namespace std;
class HasPtr {
public:
HasPtr(const string s = string()) ://构造函数
ps(new string(s)), i(0), use(new size_t(1)) {}
HasPtr(const HasPtr& rhs) :ps(rhs.ps), i(rhs.i), use(rhs.use) {
*use++;//拷贝构造函数时,递增引用计数器
}
HasPtr& operator=(const HasPtr& rhs);
~HasPtr();
private:
string* ps;
int i;
size_t* use;//引用计数器
};
HasPtr::~HasPtr() {
if (--*use == 0) {//只有当引用计数被减为0时,才释放动态内存
delete ps;
delete use;
}
}
HasPtr& HasPtr::operator=(const HasPtr& rhs) {
*rhs.use++;//=右边的引用计数递增,,所放位置很重要,也起到防自赋值效果
if (--*use == 0) {//=左边的引用计数递减为0时,释放动态内存
delete ps;
delete use;
}
ps = rhs.ps;
i = rhs.i;
use = rhs.use;
return *this;
}
2、定义行为像值的类
副本与原对象完全独立,即实现底层数据的深拷贝。
为此,实现HasPtr类需要实现以下三个特点:
i、定义一个拷贝构造函数,完成string的深拷贝,而不是拷贝指针;
ii、定义一个析构函数来释放string;
iii、定义一个拷贝赋值运算符来释放对象当前的string,并从右侧运算对象拷贝string。(防止自赋值现象)
#include<string>
using namespace std;
class HasPtr {
public:
HasPtr(const string &s =string()):
ps(new string(s)),i(0){}
HasPtr(const HasPtr& rhs):ps(new string(*rhs.ps)),i(rhs.i){}
HasPtr& operator=(const HasPtr& rhs);
~HasPtr() {
delete ps;
}
private:
string *ps;
int i;
};
HasPtr& HasPtr::operator=(const HasPtr& rhs) {
HasPtr* tmp = new string(*rhs.ps);//先深拷贝,
delete ps;//释放本对象的动态内存
ps = tmp;//赋值
i = rhs.i;
return *this;//返回本对象
}
上一篇: Tomcat server.xml详解
下一篇: c++ 11 智能指针