智能指针:C++中优雅的内存管理解决方案

本文介绍了C++中的智能指针,包括unique_ptr、shared_ptr和weak_ptr,作为避免内存泄漏的重要工具。unique_ptr是独占所有权的指针,不允许复制,适合非共享资源;shared_ptr允许多个指针共享资源,有引用计数机制;weak_ptr则用于解决shared_ptr的循环引用问题,避免内存泄漏。文章详细讲解了各个智能指针的使用场景和方法。
摘要由CSDN通过智能技术生成

在这里插入图片描述

前言

欢迎来到💖小K💖💞C++专栏💞,内存泄漏指由于疏忽或错误造成程序未能释放已经不再使用的内存的情况,这是C和C++程序员的噩梦之一。本节将为大家带来解决办法—>智能指针

在这里插入图片描述

1、简介

✨智能指针是一个模板类,封装了裸指针,可以对指针进行安全的操作。

  • 使用RAII特点,将对象生命周期使用栈来管理
  • 智能指针区分了所有权,因此使用责任更为清晰
  • 智能指针大量使用操作符重载和函数内联特点,调用成本和裸指针无差别

2、为什么要使用智能指针

✨一句话:智能指针就是帮我们C++程序员管理动态分配的内存的,它会帮助我们自动释放new出来的内存,从而避免内存泄漏!

✨内存泄漏指由于疏忽或错误造成程序未能释放已经不再使用的内存的情况,这是C和C++程序员的噩梦之一。

3、unique_ptr

✨unique_ptr是一种定义在中的智能指针(smart pointer)。它持有对对象的独有权——两个unique_ptr不能指向一个对象,不能进行复制操作只能进行移动操作。

unique_ptr对象是管理的对象的唯一拥有者:因为当unique_ptr对象释放时会删除它们的托管对象,而不考虑其他指针是否仍然指向相同的对象,从而使指向那里的其他指针指向无效的位置。

unique_ptr对象复制了有限的指针功能,通过操作符*和->(用于单个对象)或操作符[](用于数组对象)提供对其托管对象的访问。出于安全考虑,它们不支持指针算术运算,只支持移动赋值(禁用复制赋值)。

✨下面是基本的使用方法

void testOne() 
{
	unique_ptr<int> p(new int(1234));
	cout << *p << endl;
	unique_ptr<int> p1;
	//p1 = p;                     错误,不能赋值和拷贝
	//unique_ptr<int> p2(p);
	p1 = move(p);           //转交所有权
	cout << *p1 << endl;
	//cout << *p << endl;   //错误,p已经失去了管理对象

	unique_ptr<int> p3(new int(897));
	p3.reset(p1.release());
	cout << *p3 << endl;
}

✨需要手动写删除器的情况,一般是操作自定义类型(且注意unique_ptr的删除器写法的,需要手动写入删除器类型)

class MM {
public:
	~MM() {
		cout << "调用析构函数成功" << endl;
	}
};
void testTwo() 
{
	//unique_ptr删除器写法,需要手动写入删除器类型
	unique_ptr<MM,void(*)(MM*&)> Pmm(new MM[4], [](MM*& pmm) {delete[] pmm; });
}

注意当函数参数为unique_ptr类型时,需要使用引用,因为此类型是独享型,禁止拷贝

void print(unique_ptr<int>& p) 
{
	cout << *p << endl;
}

4、shared_ptr

✨unique_ptr是一个独享指针,同一时刻只能有一个unique_ptr指向一个对象,而shared_ptr是一个共享指针,同一时刻可以有多个shared_ptr指向同一对象,但会记录有多少个shared_ptr共同指向一个对象。这便是所谓的引用计数(reference counting)。

✨ 一旦最后一个这样的指针被销毁,也就是一旦某个对象的引用计数变为0,这个对象会被自动删除。这在非环形数据结构中防止资源泄露很有帮助。

基本用法

void testOne() 
{
	shared_ptr<int> p1; //无参构造
	if (!p1) 
	{
		cout << "空的智能指针对象" << endl;
	}
	shared_ptr<int> p2(new int(1234));
	//shared_ptr<int> p2 = new int(1234); 错误

	shared_ptr<int> p3 = make_shared<int>(1234);  //使用make_shared不支持创建数组
	//怎么访问数据,直接把智能指针对象当做指针来用
	cout << *p3 << endl;
	//cout << p3[0] << endl; 错误,没有下标的使用方式

	//获取管理对象原生指针
	int* k = p3.get();
	cout << *k << endl;
	cout << k[0] << endl;
	//注意:千万不要手动释放原生指针,否则会中断(释放两次)
	//delete k;

	cout << "管理对象数:" << p3.use_count() << endl;
	shared_ptr<int> p4(p3);
	cout << "管理对象数:" << p3.use_count() << endl;

}

vector管理普通指针的弊端

class MM 
{
public:
	MM(string name="",int age=0):name(name),age(age){}
	void print() 
	{
		cout << name << "\t" << age << endl;
	}
	~MM() 
	{
		cout << "调用析构函数" << endl;
	}
protected:
	string name;
	int age;
};
void testTwo() 
{
	cout <<"-----------------------------------" << endl;
	shared_ptr<MM> p(new MM("name1", 19));
	p->print();

	/*****************vector弊端*****************/
	vector<MM*> p1;
	p1.push_back(new MM("name", 20));
	//p1.pop_back();
	p1.clear();
	//可以看到pop和clear函数都不会自动调用析构函数---->当vector操作自定义类型指针的时候
	/*******************************************/

	//比较常用的用法--->工作中
	vector<shared_ptr<MM>> arr;
	shared_ptr<MM> p2(new MM("name"));
	p2->print();
	arr.push_back(p2);
}

上面代码中,当vector管理MM*类型的时候,并不会自动调用析构函数,这时候我们只需要让vector管理shared_ptr就好了

需要手动写删除器的情况,和unique_ptr一样

void testThree() 
{
	cout << "-----------------------------------" << endl;
	shared_ptr<int> king(new int[4] {1, 2, 3, 4});
	int* pNum = king.get();
	for (int i = 0; i < 4; i++)
	{
		cout << pNum[i] << " ";
	}
	cout << endl;

	//当操作特殊类型(自定义类型,c语言文件的打开)的时候,需要手动写删除器
	shared_ptr<MM> p1(new MM[4], [](MM*& pmm) {delete[]pmm; });
	shared_ptr<FILE> pfile(fopen("1.txt", "w+"), [](FILE*& file) {free(file); });
}

✨当shared_ptr当函数参数和返回值的时候,正常写就ok

void pprint(shared_ptr<MM>& mm) {
	mm->print();
}
shared_ptr<int> returnpoint(int num) {
	return shared_ptr<int>(new int(num));
}

5、weak_ptr

shared_ptr可以用来避免内存泄漏,可以自动释放内存。但是在使用中可能存在循环引用,使引用计数失效,从而导致内存泄漏的情况。如下代码所示:

class B;
class A {
public:
	~A() {
		cout << "A" << endl;
	}
	shared_ptr<B> a;
};
class B {
public:
	~B() {
		cout << "B" << endl;
	}
	shared_ptr<A> b;
};
void testOne() 
{
	shared_ptr<A> ao(new A);
	shared_ptr<B> bo(new B);
	ao->a = bo;
	bo->b = ao;
}

我们发现上面的一段代码中,ao中的指针管理bo,bo中的指针管理ao,形成了循环管理,这时候就不会自动释放。这时候我们只需要把shared_ptr改为weak_ptr就ok了

基本处理

weak_ptr只能从shared_ptr或者已有的weak_ptr去构造,不能直接管理对象
不能直接访问管理的对象,访问数据要通过lock()函数去访问shared_ptr,然后再去访问数据

void testTwo() 
{
	shared_ptr<int> p(new int(1234));
	cout << "count:" << p.use_count() << endl;
	weak_ptr<int> w(p);
	cout << "count:" << w.use_count() << endl;
	weak_ptr<int> w2(w);
	cout << "count:" << w2.use_count() << endl;
	cout << *w.lock() << endl;
	shared_ptr<int> k = w2.lock();
	cout << *k << endl;
}
  • 33
    点赞
  • 28
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 24
    评论
评论 24
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

xiaok-cpp

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值