先前的文章我们讲过了智能指针的概念等一系列问题,下面我们直接来看智能指针
1.带有引用计数的智能指针 (Shared_ptr)同时也是强智能指针
所谓的引用计数,就是有多少个智能指针对象管理这个堆内存。引用计数的作用是做所有对象共享的。
不可以写为普通的成员变量,要写成静态的成员变量
我们用一个引用计数管理器来实现Shared_ptr智能指针:
设计思路:
将引用计数管理器设计为数组,用地址和引用计数来当作节点记录堆内存,因为对地址只做一个简单的计数管理,所以用void*,引用计数用int,在智能指针类中,给出一个私有的成员变量mptr,一个静态的成员变量(引用计数管理器)static RefManage。给定一个对象rm。
但智能指针与引用计数管理器属于两种不同的类,为了实现它们之间的信息交互就要调用对方的接口。在引用计数管理器中预留一些接口(下面会给出接口的类型),注意堆上的操作都在智能指针类,通过下面的几个接口可以来进行对
智能指针类的操作,智能指针类中成员变量,静态变量已经设置好。智能指针类构造函数中,引用计数初值都是零,现在添加
一个引用计数,现在生成一个sp1对象,要调用构造函数对sp1进行初始化,将mptr传给地址域,mptr地址为0x100,通过
getRef()接口传递到引用计数管理器类中,要将0x100与地址域中的数据做比较,如查到与0x100相等的数据,则表明有智能指
针正在引用这个数据,对引用计数+1;如没查到就将0x100放到地址域中,放到新的节点去,引用计数加1就好了。
当sp1生存周期到后,应该先减引用计数,如果减为零就释放。
接口:
- addRef():增加引用计数
- delRef(): 减引用计数
- getRef(): 获取引用计数
代码实现:
//引用计数管理器类
class RefManage
{
public:
static RefManage* getInstance();
{
}
private:
//初始化
RefManage(): length(0)
{}
RefManage(const RefManage& rhs);
public:
void addRef(void* ptr)
{
if(ptr != NULL)
{
int index = Find(mptr);
if(index < 0)
{
/*
arr[length].addr = ptr;
arr[length].refcount++;
*/
Node tmp(ptr ,1);
arr[length++] = tmp;
}
else
{
arr[index].refcount++;
}
}
}
void delRef(void* ptr)
{
if(ptr != NULL)
{
int index = Find(ptr);
if(index < 0)
{
throw exception("addr is not exist")
}
else
{
if(arr[index].refcount != 0)
{
arr[index].refcount--;
}
}
}
}
int getRef(void* ptr)
{
if(ptr != NULL)
{
return 0;
}
int index = Find(ptr);
if(index < 0)
{
return -1;
}
else
{
return arr[index].refcount;
}
}
private:
//对内接口
int Find(void * mptr)
{
for(int ℹ️= 0 ;ℹ️<length;++i)
{
if(arr[I].addr == ptr)
{
return ℹ️;
}
}
return -1;
}
class Node
{
public:
Node(void * ptr, int ref = 0)
{
addr = ptr;
refcount = ref;
}
Node()
{
memset(this,0,sizeof(Node));
}
public:
void* adds;
int refcount;
};
//成员变量
NOde arr[10];
int length;//有效节点个数。当前要插入的节点下标
static RefManage rm;
};
RefManage RefManage::rm;
//智能指针类
class Shared_ptr
{
public:
Shared_ptr(T* ptr) :mptr(ptr)
{
AddRef();//调用引用计数管理器类中的addRef接口
}
Shared_ptr(const Share_ptr<T>& rhs):mptr(rhs.mptr)
{
AddRef();
}
Shared_ptr<T>& operator = (const Shared_ptr<T>& rhs)
{
if(this != &rhs)
{
if(GetRef() == 0)
{
delete mptr;
}
mptr = rhs.mptr;
AddRef();
}
return *this;
}
~Shared_ptr()
{
Delref();
if(GetRef() == 0)
{
delete mptr;
}
mptr = NULL;
}
T* operator->()
{
return mptr;
}
T& operator*()
{
return *mptr;
}
private:
void AddRef()
{
rm->addRef(mptr);
}
void delRef()
{
rm->delRef(mptr);
}
int GetRef()
{
return rm->getRef(mptr);
}
T* mptr;
static RefManage* rm;
};
//类外初始化
template<typename T>
RefManage* Shared_ptr<T>:: rm = RefManage::getInstance();
int main()
{
int *p = new int;
Shared_ptr<int> sp1(p);
Shared_ptr<int> sp2(p);
Shared_ptr<int> sp3(p);
Shared_ptr<int> sp4(p);
Shared_ptr<int> sp1(new int);
Shared_ptr<int> sp2(sp1);
return 0;
}
具体的分布:
1、创建shared_ptr实例
最安全和高效的方法是调用make_shared库函数,该函数会在堆中分配一个对象并初始化,最后返回指向此对象的share_ptr实例。如果你不想使用make_ptr,也可以先明确new出一个对象,然后把其原始指针传递给share_ptr的构造函数。
示例如下:
int main() {
// 传递给make_shared函数的参数必须和shared_ptr所指向类型的某个构造函数相匹配
shared_ptr<string> pStr = make_shared<string>(10, 'a');
cout << *pStr << endl; // aaaaaaaaaaint *p = new int(5);
shared_ptr<int> pInt(p);
cout << *pInt << endl; // 5
}
2、访问所指对象
shared_ptr的使用方式与普通指针的使用方式类似,既可以使用解引用操作符*获得原始对象进而访问其各个成员,也可以使用指针访问符->来访问原始对象的各个成员。
3、拷贝和赋值操作
我们可以用一个shared_ptr对象来初始化另一个share_ptr实例,该操作会增加其引用计数值。
例如:
int main() {
shared_ptr<string> pStr = make_shared<string>(10, 'a');
cout << pStr.use_count() << endl; // 1shared_ptr<string> pStr2(pStr);
cout << pStr.use_count() << endl; // 2
cout << pStr2.use_count() << endl; // 2
}
如果shared_ptr实例p和另一个shared_ptr实例q所指向的类型相同或者可以相互转换,我们还可以进行诸如p = q这样赋值操作。该操作会递减p的引用计数值,递增q的引用计数值。
例如:
class Example
{
public:
Example(string n) : name(n) { cout << n << " constructor..." << endl; }
~Example() { cout << name << " destructor..." << endl; }string name;
};int main() {
shared_ptr<Example> pStr = make_shared<Example>("a object");
shared_ptr<Example> pStr2 = make_shared<Example>("b object");
cout << pStr.use_count() << endl;
cout << pStr2.use_count() << endl;pStr = pStr2; // 此后pStr和pStr指向相同对象
cout << pStr->name << endl;
cout << pStr2->name << endl;
}
输出如下:
a object constructor...
b object constructor...
a object destructor...
b object
b object
b object destructor...
4、检查引用计数
shared_ptr提供了两个函数来检查其共享的引用计数值,分别是unique()和use_count()。
在前面,我们已经多次使用过use_count()函数,该函数返回当前指针的引用计数值。值得注意的是use_count()函数可能效率很低,应该只把它用于测试或调试。
unique()函数用来测试该shared_ptr是否是原始指针唯一拥有者,也就是use_count()的返回值为1时返回true,否则返回false。
示例:
int main() {
shared_ptr<string> pStr = make_shared<string>(10, 'a');
cout << pStr.unique() << endl; // trueshared_ptr<string> pStr2(pStr);
cout << pStr2.unique() << endl; // false;
}