C++智能指针

1. 智能指针

1.1 知识点

1.1.1 为什么要提出智能指针?

实际开发中:

1)用户开辟了堆空间,没有及时释放堆空间资源,可能会造成内存泄露

2)用户开辟了堆空间,也及时释放了堆空间资源,但该空间其它对象还在使用,会出现程序崩溃的现象

作用:

用于帮助用户去管理堆空间

1.1.2 智能指针与普通指针有什么区别?

智能指针体现出智能化(可以帮助用户自动管理堆内存空间),当引用计数机制等于0的时候,智能指针会自动释放所管理的堆空间资源

在C++中智能指针它不是一个指针,他是一个类(模板类),该类重载了指针的运算,例如:->、*、++、--等等,形象的称呼"智能指针"

1.1.3 C++提供的智能指针有四个:用于管理裸指针申请的堆空间

shared_ptr:共享指针

weak_ptr:弱指针

unique_ptr:唯一指针

C++11标准舍弃:auto_ptr:自动指针

2 shared_ptr共享指针

2.1 知识点

该指针可以允许其它多个智能指针一起管理同一块内存空间

如何使用:

创建共享指针,共享指针会有两个空间,一个空间是管理的堆空间,另外一个空间是存放计数值

计数值:

共享指针引入的引用计数机制,计数值里存放的是有多少个智能指针管理同一块堆空间,即新增一个智能指针管理堆空间,那么引用计数+1,减少一个智能指针管理堆空间,那么引用计数-1

方法:

注:多个共享指针使用同一个引用计数空间

share1.use_count()//查看引用计数值

share1.swap(share4);//交换管理的空间

share1.reset();//释放管理权

get()//获取所管理的堆空间的地址

shared_ptr.cpp

cpp 复制代码
#include "iostream"
using namespace std;

//智能指针shared_ptr(共享智能指针)
int main()
{
	int* p = new int(10);//使用裸指针开辟的堆空间
	int* q = new int(50);

	//将我们开辟的指针p指向的空间 交给共享智能指针share1来管理
	shared_ptr<int> share1(p);
	shared_ptr<int>share2 = share1;//share2也来管理p指针指向的空间
	shared_ptr<int>share3(share1);//拷贝构造函数

	shared_ptr<int> share4(q);//share4也来管q指针指向的空间


	cout << "引用计数值:" << share1.use_count() << endl;  //3
	cout << endl;

	cout << "空间中的值:" << *share1 << endl;
	cout << endl;

	//查看空间地址
	cout << "share1管理空间的地址:" << share1 << endl;
	cout << "share2管理空间的地址:" << share2 << endl;
	cout << "share3管理空间的地址:" << share3 << endl;
	cout << "p指向空间的地址:" << p << endl;

	cout << endl;
	//.get()也可以查看管理的地址
	cout << "share1管理空间的地址:" << share1.get() << endl;
	cout << "share2管理空间的地址:" << share2.get() << endl;
	cout << "share3管理空间的地址:" << share3.get() << endl;
	cout << "p指向空间的地址:" << p << endl;

	cout << endl;
	//交换两个共享指针所管理的堆空间权力
	cout << "share1管理空间的地址:" << share1.get() << endl;
	cout << "share4管理空间的地址:" << share4.get() << endl;

	cout << endl;
	share1.swap(share4);//交换管理的空间

	cout << "share1管理空间的地址:" << share1.get() << endl;
	cout << "share4管理空间的地址:" << share4.get() << endl;

	cout << endl;

	//释放堆空间的管理权
	share1.reset();//释放管理权,这里释放的share1是q的空间权力,因为我们前面交换了share1和share4的管理的空间
	cout << "share1管理空间的地址:" << share1.get() << endl;

	cout << endl;
	cout << "引用计数值:" << share2.use_count() << endl;  //3
	
	return  0;
}

2.2 管理自定义类型

cpp 复制代码
//智能指针shared_ptr(共享智能指针),管理自定义类型
class People {
public:
	int number = 5;
};
int main()
{
	People* people = new People;//将类的实例开辟在堆上
	shared_ptr<People> share1(people);
	cout << "number的值为:" << share1->number << endl;

	return  0;
}

2.3 手动实现shared_ ptr共享指针

例如:

解引用

取成员操作

引用计数操作等

myself_shared_ptr.cpp

cpp 复制代码
#include "iostream"
using namespace std;

// 手动实现shared_ ptr共享指针
//例如:
//解引用
//取成员操作
//引用计数操作等

class People {
public:
	int num = 123;
};

template<class T> class share_ptr {
public:

	share_ptr(T* data) {//构造函数
		this->ptr = data;
		refCount = new int(1);
	}

	//重载* 解引用操作
	T operator * () {
		return *ptr;
	}

	//重载取成员操作
	T* operator ->() {
		return ptr;
	}
	

	// 引用计数操作
    int use_count() const {
        return *refCount;//返回计数值
    }

	// 拷贝构造函数  
	share_ptr(const share_ptr<T>& other)
	{
		ptr = other.ptr;
		refCount = other.refCount;
		(*other.refCount)++;    // 引用计数加 1
	}

	// 重载赋值运算符           //传入要赋值的对象     
	void operator =(const share_ptr<T>& other)
	{
		// 防止自赋值
		if (this != &other)
		{
			// 释放资源
			ptr = other.ptr;
			refCount = other.refCount;
			(*other.refCount)++;    // 引用计数加 1
		}
	}

	// 释放资源
	void my_release()
	{
		(*refCount)--;
		if (*refCount == 0)
		{
			delete ptr;
			delete refCount;
		}
	}


	//析构函数
	~share_ptr() {
		my_release();
	}
	
	T* ptr; //存储要管理的对象空间
	int* refCount; //计数值

};

int main()
{
	int* p = new int(22);
	share_ptr<int> share1(p);
	cout << "值为:" << *share1 << endl;
	cout << "引用计数值为:" << share1.use_count() << endl;

	People* people = new People;
	share_ptr<People> share2(people);
	cout << "people类的num值为:" << share2->num << endl;
	cout << "引用计数值为:" << share2.use_count() << endl;


	//调用拷贝构造函数
	share_ptr<int> share3(share1);
	cout << "引用计数值为:" << share1.use_count() << endl;

	//调用释放函数
	share3.my_release();
	cout << "引用计数值为:" << share1.use_count() << endl;
	

	share_ptr<int> share4 = share1;
	share_ptr<int> share5 = share1;
	cout << "引用计数值为:" << share1.use_count() << endl; //3

	//调用释放函数
	share4.my_release();
	cout << "引用计数值为:" << share1.use_count() << endl;//2

	return 0;
}

3 weak_ptr弱指针

weak_ptr提出是辅助shared_ptr使用

问题:

使用共享指针会造成资源被相互占用,而得不到释放的问题

提出:

弱指针来解决该问题

3.1 问题引入

weak_ptr.cpp

cpp 复制代码
#include "iostream"
using namespace std;


//先演示shared_ptr指针的问题
class Person;//提前声明以下Person类防止,报未定义错误(以为我们在people中使用了Person类)
class People {
public:
	shared_ptr<Person> share1;
	~People() {
		cout << "释放People的资源" << endl;
	}
};

class Person {
public:
	shared_ptr<People> share2;
	~Person() {
		cout << "释放Person的资源" << endl;
	}
};

int main()
{
	//实例化两个对象
	People* people = new People;
	Person* person = new Person;
	//时share11  share22分别管理对应的空间
	shared_ptr<People> share11(people);//管理people
	shared_ptr<Person> share22(person);//管理person
	
	//接着给对象中的属性赋值,使其属性也管理相应的空间
	people->share1 = share22;//使people中的share1属性管理person空间
	person->share2 = share11;//使person中的share2属性管理people空间


	//2  ,并且两个类的析构函数都没调用,空间没有得到释放
	//这是因为在类中share1管理了person空间, 而share2有管理了people空间,双方都在等待对方使用完成后释放空间
	//两个对象相互持有彼此的 shared_ptr,导致了循环引用,从而阻止了析构函数的调用。
	cout << "引用计数值:" << share11.use_count() << endl;

	return 0;
}
问题:

两个类的析构函数都没调用,空间没有得到释放

这是因为在类中share1管理了person空间, 而share2有管理了people空间,双方都在等待对方使用完成后释放空间,两个对象相互持有彼此的 shared_ptr,导致了循环引用,从而阻止了析构函数的调用。

问题解析:

即当 People 类的 shared_ptr 指向的 Person 对象超出作用域时,Person 类的 shared_ptr 也

会因为指向 People 对象而保持有效。这导致了一种看似超出作用域但实际上引用计数不为零

的情况,从而阻止了对象的析构。

具体来说,在你的代码中,当 People 类的 shared_ptr 被销毁时,它会尝试释放 Person 对

象。但由于 Person 对象的 shared_ptr 仍然持有对 People 对象的引用,因此 Person 对象并

不会真正释放。同样,当 Person 类的 shared_ptr 被销毁时,它会尝试释放 People 对象,但

由于 People 对象的 shared_ptr 仍然持有对 Person 对象的引用,所以 People 对象也不会真

正释放。

(以上讲人话就是,person要释放空间,但是people还管理person呢,他就认为自己可能还活着,反过来就是people要释放空间,但是person还管理people呢,他也认为自己还活着,所以导致都释放不了空间)

3.2 问题解决---使用weak_ptr

使用 weak_ptr 可以解决这个问题,因为 weak_ptr 不会增加引用计数,它可以观察 shared_ptr 而不影响对象的生命周期。这样,当最后一个 shared_ptr 超出作用域时,对象的引用计数会减少到零,从而正确地触发析构函数。

weak_ptr.cpp

cpp 复制代码
//weak_ptr弱指针,来解决上述问题
class Person;//提前声明以下Person类防止,报未定义错误(以为我们在people中使用了Person类)
class People {
public:
	weak_ptr<Person> share1;
	~People() {
		cout << "释放People的资源" << endl;
	}
};

class Person {
public:
	weak_ptr<People> share2;
	~Person() {
		cout << "释放Person的资源" << endl;
	}
};

int main()
{
	//实例化两个对象
	People* people = new People;
	Person* person = new Person;
	//时share11  share22分别管理对应的空间
	shared_ptr<People> share11(people);//管理people
	shared_ptr<Person> share22(person);//管理person

	//接着给对象中的属性赋值,使其属性也管理相应的空间
	people->share1 = share22;//使people中的share1属性管理person空间
	person->share2 = share11;//使person中的share2属性管理people空间


	cout << "引用计数值:" << share11.use_count() << endl;

	return 0;
}

空间得到正确释放

3.3 weak_ptr的使用

切记weak_ptr不会是计数空间增加

weak.expired()

检查 weak 是否已经过期。如果 weak 过期,意味着它不再管理任何资源,即 shared_ptr 已经释放了它所持有的资源。

weak.lock()

weak_ptr 的 lock 方法用于尝试将 weak_ptr 转换为 shared_ptr,如果转换成功,它会返 回一个指向相同资源的 shared_ptr,否则返回一个空指针。

weak_ptr.cpp

cpp 复制代码
//weak_ptr弱指针基本使用
class People {
public:

	~People() {
		cout << "释放People的资源" << endl;
	}
};

int main()
{
	//实例化两个对象
	People* people = new People;
	//时share11  share22分别管理对应的空间
	shared_ptr<People> share11(people);//管理people

	weak_ptr<People>weak = share11;//weak_ptr不会是计数空间增加

	cout << "引用计数值:" << share11.use_count() << endl; //1

	//通过调用 expired 方法来检查 weak 是否已经过期。如果 weak 过期,
	// 意味着它不再管理任何资源,即 shared_ptr 已经释放了它所持有的资源。
	if (weak.expired())
	{
		cout << "资源空间已被释放" << endl;
	}

	//尝试从 weak 中获取一个有效的 shared_ptr。如果 weak 仍然管理着资源(People 对象),则返回一个指向该资源的 shared_ptr。
	//如果 weak 不再管理资源(资源已被释放或 weak 已经过期),则返回一个空指针。
	shared_ptr<People>share5=weak.lock();//注意这里计数空间+1
	if (share5 == nullptr)
	{
		cout << "没有管理任何堆空间" << endl;
	}

	cout << "引用计数值:" << share11.use_count() << endl;//2


	return 0;
}

4. unique_ptr 唯一指针

称为"独占式指针",用unique_ptr去管理空间,那么只允许一个智能指针管理一个空间

确保同一时间只有一个 unique_ptr 可以拥有对对象的所有权。

由于 unique_ptr 拥有独特的所有权,它不支持拷贝语义

weak_ptr.cpp

cpp 复制代码
//unique_ptr 唯一指针
int main()
{
	int* p = new int(50);
	unique_ptr<int> unique1(p);
	//unique_ptr<int> unique2(unique1);//报错,unique_ptr只允许一个管理
	//unique_ptr<int> unique2 = unique1;//报错,unique_ptr只允许一个管理
	cout << *unique1 << endl;

	cout << "p的地址" << p << endl;
	cout << "unique1管理的地址" << unique1 << endl;

	return 0;
}
相关推荐
小飞猪Jay26 分钟前
C++面试速通宝典——13
jvm·c++·面试
Kalika0-038 分钟前
猴子吃桃-C语言
c语言·开发语言·数据结构·算法
代码雕刻家1 小时前
课设实验-数据结构-单链表-文教文化用品品牌
c语言·开发语言·数据结构
龙图:会赢的1 小时前
[C语言]--编译和链接
c语言·开发语言
sp_fyf_20241 小时前
计算机前沿技术-人工智能算法-大语言模型-最新研究进展-2024-10-02
人工智能·神经网络·算法·计算机视觉·语言模型·自然语言处理·数据挖掘
rjszcb1 小时前
一文说完c++全部基础知识,IO流(二)
c++
韩楚风2 小时前
【linux 多进程并发】linux进程状态与生命周期各阶段转换,进程状态查看分析,助力高性能优化
linux·服务器·性能优化·架构·gnu
陈苏同学2 小时前
4. 将pycharm本地项目同步到(Linux)服务器上——深度学习·科研实践·从0到1
linux·服务器·ide·人工智能·python·深度学习·pycharm
Ambition_LAO2 小时前
解决:进入 WSL(Windows Subsystem for Linux)以及将 PyCharm 2024 连接到 WSL
linux·pycharm
小字节,大梦想2 小时前
【C++】二叉搜索树
数据结构·c++