目录
概念
共享指针是可以 由多个栈上智能指针对象 同时托管同一堆上资源的。
因为shared_ptr的内部实现引用计数来管理有多少智能指针指向了这块堆上资源。
当一个共享智能指针出栈时,引用计数会自动-1。当最后一个共享智能指针出栈引用计数为0时,代表没有智能指针对象托管这块堆上资源了,此时将执行释放对上资源的操作
使用标准库共享智能指针
引入头文件:#include <memory>
创建Stu类:
#include <iostream>
#include <memory>
using namespace std;
class Stu
{
private:
string name;
int age;
public:
Stu(string name, int age)
{
this->name = name;
this->age = age;
cout << "Stu的构造" << endl;
}
void showInfo()
{
cout << "姓名:" << this->name << ",年龄:" << this->age << endl;
}
~Stu()
{
cout << "Stu的析构" << endl;
}
};
使用标准库智能指针:
int main()
{
//使用系统方法
shared_ptr<Stu> ptr1 (new Stu("zhangsan", 20));
ptr1->showInfo();
cout << "ptr1.count=" << ptr1.use_count() << endl; //>>ptr1.count=1
shared_ptr<Stu> ptr2 = ptr1; //拷贝构造创建新共享指针
shared_ptr<Stu> ptr3;
ptr3 = ptr2; //=号赋值创建新共享指针
ptr1->showInfo();
cout << "ptr1.count=" << ptr1.use_count() << endl; //>>ptr1.count=3
ptr2->showInfo();
cout << "ptr2.count=" << ptr1.use_count() << endl; //>>ptr2.count=3
ptr3->showInfo();
cout << "ptr3.count=" << ptr1.use_count() << endl; //>>ptr1.count=3
//shared_ptr是共享智能指针,所以三个ptr打印内容相同.
cout << "---------------" << endl;
//析构一个共享智能指针,看看count变化
ptr1.~shared_ptr();
cout << "ptr3.count=" << ptr3.use_count() << endl; //>>ptr3.count=2
cout << "ptr4.count=" << ptr1.use_count() << endl; //>>ptr4.count=2
cout << sizeof (ptr3) << endl; //16,是两个指针的大小
cout << sizeof (ptr1) << endl; //16
cout << "-------------------------" << endl;
return 0;
}
运行结果:
共享智能指针底层实现
先要理解共享智能指针的实质。
共享智能指针实际上是由两个类组成:一个计数器类,一个指针类。
计数器类(代码片段):
template <class T>
class RefCount
{
private:
T * ptr; //用来指向堆区Stu对象
int count; //用来计数
指针类(代码片段):
template<class T>
class SHARED_ptr
{
private:
T * ptr; //用来指向堆区Stu对象
RefCount<T> * refcount; //类中嵌套 类的指针, 用来指向计数器类
即指针类中包含计数器类的指针。
当多个共享智能指针指向同一片空间时,实际上是由多个指针类和一个计数器类共同指向该堆区空间。
所以改变计数器类的值,所有通过指针类查看的计数器值都是一样的。
自定义底层实现时,需注意:在拷贝构造、=号赋值时,对指针类中计数器指针的处理,并非时将外类计数器的值复制给本类计数器的值,而是将本类计数器指针,指向外类计数器指针地址,达到共用同一个计数的目的。
自己实现shared_ptr类,需两部分,计数器类和指针类,Stu类仍用上边的。
自定义计数器类代码:
//计数器类
template <class T>
class RefCount
{
private:
T * ptr; //用来指向堆区Stu对象
int count; //用来计数
public:
//构造函数
RefCount(T* ptr = nullptr)
{
this->ptr = ptr;
if(this->ptr != nullptr)
{
this->count = 1;
}
else{
this->count = 0;
}
}
//增加引用计数
void addRef()
{
this->count++;
}
//减少引用计数
int delRef()
{
//这里把计数器返回出去的目的是,
//当减完后,可直接判断计数器,如果为0,就直接释放空间
//省了一步get_count
return --this->count;
}
//获取引用计数
int get_count()
{
return this->count;
}
};
自定义指针类:
//SHARED_ptr的类模板
template<class T>
class SHARED_ptr
{
private:
T * ptr; //用来指向堆区Stu对象
RefCount<T> * refcount; //类中嵌套 类的指针, 用来指向计数器类
public:
//SHARED_ptr的构造
SHARED_ptr(T * ptr = nullptr)
{
this->ptr = ptr;
if(this->ptr != nullptr)
{
this->refcount = new RefCount<T>(this->ptr);
}
else{
this->refcount = new RefCount<T>();
}
}
//SHARED_ptr的析构
~SHARED_ptr()
{
//每个对象析构的时候,都让计数器--,并判断计数器是否为0,
//如果计数器为0了,说明析构的是最后一个对象,那么就释放空间
if(this->refcount->delRef() == 0){
delete ptr;
delete refcount;
this->ptr = nullptr;
this->refcount = nullptr;
}
}
//SHARED_ptr的拷贝构造
SHARED_ptr(const SHARED_ptr& other)
{
//如果外类不为空
if(other.ptr != nullptr)
{
//将外类堆地址赋值给本类的指针
this->ptr = other.ptr;
//错误理解:将外类的计数器,赋值给本类计数器<<--这种理解是错误的
//正确理解:将本类的计数器指针,也指向外类计数器的地址
this->refcount = other.refcount;
//拷贝构造调用时机是在初始化类对象时,所以本类计数器+1
this->refcount->addRef();
}
//如果外类为空
else {
this->ptr = other.ptr; //将外类堆地址赋值给本类的指针
this->refcount = other.refcount; //将外类的计数器,赋值给本类计数器
}
}
//SHARED_ptr的=号运算符重载
SHARED_ptr& operator = (const SHARED_ptr& other)
{
if(this == &other)
{
return *this;
}
//外类为空
if(other.ptr == nullptr)
{
//外类空,本类非空
if(this->ptr != nullptr)
{
//本类计数器-1,如果减后计数器为0,
//说明堆空间没人用了,该回收了
if(this->refcount->delRef() == 0)
{
delete this->ptr;
delete this->refcount;
this->ptr = nullptr;
this->refcount = nullptr;
}
}
//外类赋值给本类
this->ptr = other.ptr;
this->refcount = other.refcount;
}
//外类非空
else {
//外类非空,本类非空
if(this->ptr != nullptr)
{
//两个类都非空,先对本类进行计数器-1,
//如果计数器为0了,那就释放堆区资源
if(this->refcount->delRef() == 0)
{
delete this->ptr;
delete this->refcount;
this->ptr = nullptr;
this->refcount = nullptr;
}
}
//然后外类对本类赋值
this->ptr = other.ptr;
this->refcount = other.refcount;
//赋值后,本类计数器需要+1
this->refcount->addRef();
}
return *this;
}
//取*运算符重载
T& operator*()
{
return *ptr;
}
//get方法
T* get()
{
return ptr;
}
//指针运算符重载
T* operator->()
{
return ptr;
}
//获取引用记数
int use_count()
{
return refcount->get_count();
}
};
效果实现:
int main()
{
//使用自定义共享智能指针
SHARED_ptr<Stu> ptr4 (new Stu("zhangsan", 20));
ptr4->showInfo();
cout << "ptr4.count=" << ptr4.use_count() << endl; //>>ptr4.count=1
SHARED_ptr<Stu> ptr5 = ptr4;
SHARED_ptr<Stu> ptr6;
ptr6 = ptr5;
ptr4->showInfo();
cout << "ptr4.count=" << ptr4.use_count() << endl; //>>ptr4.count=3
ptr5->showInfo();
cout << "ptr5.count=" << ptr5.use_count() << endl; //>>ptr5.count=3
ptr6->showInfo();
cout << "ptr6.count=" << ptr6.use_count() << endl; //>>ptr6.count=3
cout << "----------------------------" << endl;
//析构一个智能指针,看看count变化
ptr4.~SHARED_ptr();
cout << "ptr6.count=" << ptr6.use_count() << endl; //>>ptr6.count=2
cout << "ptr4.count=" << ptr4.use_count() << endl; //>>ptr4.count=2
cout << sizeof (ptr6) << endl; //16,是两个指针的大小
cout << sizeof (ptr4) << endl; //16
return 0;
}
运行结果:
再次强调易错误区:
误区: 本类计数器+1了,但是其他共享智能指针的计数呢?
解答: 共享智能指针由 计数器类 和 SHARED_ptr类 两部分组成,,多个共享智能指针指向同一片空间,实际上是一个计数器对象和多个SHARED_ptr对象。 每次的赋值操作,都是让SHARED_ptr中的计数器指针,指向该唯一的计数器类。所以,只让本类的计数器+1,所有的共享智能指针都会更新。