【初阶与进阶C++详解】第二十四篇:智能指针,2024年最新大数据开发未来路在何方

先自我介绍一下,小编浙江大学毕业,去过华为、字节跳动等大厂,目前阿里P7

深知大多数程序员,想要提升技能,往往是自己摸索成长,但自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年最新大数据全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友。
img
img
img
img
img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上大数据知识点,真正体系化!

由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,并且后续会持续更新

如果你需要这些资料,可以添加V获取:vip204888 (备注大数据)
img

正文

template
class SmartPtr
{
public:
SmartPtr(T* ptr)
:_ptr(ptr)
{}

  ~SmartPtr()
  {
  	cout << "delete" << _ptr << endl;
  	delete _ptr;
  	_ptr = nullptr;
  }

//直接打印地址
T* Get()
{
return _ptr;
}
private:
T* _ptr;//指针对象
};



💎三、智能指针的使用和原理

智能指针的使用跟普通指针类似,可以使用运算符“ * " 和 ” -> “去获得指向的对象,因此,我们就需要在类中重载” * " 和" -> "函数

T& operator\*()
{
return \*_ptr;
}

T\* operator->()
{
return _ptr;
}

当程序结束时,此时sp1和sp2指针被销毁时,对象sp1和sp2会自动调用析构函数去释放所指向的资源,这是智能指针特点。

int main ()
{
//下面两个释放完后会打印delete
SmartPtr<int> sp1(new int);
SmartPtr<int> sp2(new int);
}

sp1和sp2指向的同一块空间,当sp2被销毁时,它会调用它的析构函数去delete该资源对象,当sp1被销毁时,也会去调用它的析构函数去释放sp1所指向的资源.所以,当程序结束时,sp2被先被销毁,同时释放sp2所指向的资源,然后sp1被销毁,也去释放该资源对象,那么如下的资源对象同时被释放两次,所以程序就会被崩溃掉。(资源对象被释放后,如果再去释放该资源,程序就会崩溃)

int main ()
{
	SmartPtr<int> sp1(new int);
	SmartPtr<int> sp2(sp1);
}

不能使用原生的拷贝构造函数和赋值重载函数,并且定义的拷贝构造函数和赋值重载函数需要考虑只能释放一次资源对象。

💎四、auto_ptr

auto_ptr是c++98版本库中提供的智能指针,该指针解决上诉的问题采取的措施是管理权转移的思想,也就是原对象拷贝给新对象的时候,原对象就会被设置为nullptr,此时就只有新对象指向一块资源空间。

如果auto_ptr调用拷贝构造函数或者赋值重载函数后,如果再去使用原来的对象的话,那么整个程序就会崩溃掉(因为原来的对象被设置为nullptr)。

准委员会建议:什么情况下都不要使用auto_ptr

int main ()
{
	auto_ptr<int> sp1(new int);
	auto_ptr<int> sp2(sp1);//sp1置为空
}

auto_ptr的拷贝构造函数和赋值重载函数的实现

template<class T>
class auto\_ptr
	{
	public:	

		// sp2(sp1)
		auto\_ptr(auto_ptr<T>& sp)
			:\_ptr(sp._ptr)
		{
			sp._ptr = nullptr;
		}
	  auto_ptr<T> operator=(auto_ptr<T>& sp)
		{
			if (_ptr != sp._ptr)
			{
				_ptr = sp._ptr;
				sp._ptr = nullptr;
			}

			return _ptr;
		}
	private:
		T\* _ptr;
	};

💎五、unique_ptr

unique_ptr是c++11版本boost库中提供的智能指针(后面share_ptr和weak_ptr也是这个库中),它直接将拷贝构造函数和赋值重载函数给禁用掉,因此,不让其进行拷贝和赋值。

int main()
{
	unique_ptr<int> up1(new int);
	//bit::unique\_ptr<int> up2(up1);//不能调用unique\_ptr的拷贝构造
	unique_ptr<int> up2(new int);
	//up1 = up2;//不能调用unique\_ptr的拷贝构造
}

unique_ptr的拷贝函数和赋值重载函数

template<class T>
class unique\_ptr
	{
	public:
		unique\_ptr(T\* ptr)
			:\_ptr(ptr)
		{}

		~unique\_ptr()
		{
			if (_ptr)
			{
				cout << "delete" << _ptr << endl;
				delete _ptr;
				_ptr = nullptr;
			}
		}
 	//直接返回地址
		T\* get()
		{
			return _ptr;
		}
		unique\_ptr(const unique_ptr<T>& sp) = delete;
		unique_ptr<T>& operator=(const unique_ptr<T>& sp) = delete;

	private:
		T\* _ptr;
	};

💎六、shared_ptr

🏆1.shared_ptr的基本概念

shared_ptr是c++11版本库中的智能指针,shared_ptr允许多个智能指针可以指向同一块资源,并且能够保证共享的资源只会被释放一次,因此是程序不会崩溃掉。

🏆2.shared_ptr的原理

shared_ptr采用的是引用计数原理来实现多个shared_ptr对象之间共享资源

  • shared_ptr在内部会维护着一份引用计数,用来记录该份资源被几个对象共享。
  • 当一个shared_ptr对象被销毁时(调用析构函数),析构函数内就会将该计数减1。
  • 如果引用计数减为0后,则表示自己是最后一个使用该资源的shared_ptr对象,必须释放资源。
  • 如果引用计数不是0,就说明自己还有其他对象在使用,则不能释放该资源,否则其他对象就成为野指针。

🏆3.shared_ptr的实现

赋值过程有以下三种情况:

  • ptr1=ptr1;智能指针自己给自己赋值,不做处理
  • ptr2=ptr1;如果ptr1和ptr2指向同一块空间,不做处理
  • ptr2=ptr1;如果ptr2和ptr1指向的空间不一样,如果ptr2原本就有指向的空间,此时就需要将ptr2的引用计数减1,如果此时ptr2引用计数变为0,则需要释放ptr2指向资源,然后将ptr2指向ptr1指向的资源,且ptr1的引用计数加1

🏆4.shared_ptr的循环引用

假设我们要使用定义一个双向链表,如果我们想要让创建出来的链表的节点都定义成shared_ptr智能指针,那么也需要将节点内的_pre和_next都定义成shared_ptr的智能指针。

  • 当创建出node1和node2智能指针对象时,引用计数都是1.
  • 当node1的next指向node2所指向的资源时,node2的引用计数就+1,变成2,node2的pre指向noede1所指向的资源时,node1的引用计数+1,变成2.
  • 当这两个智能指针使用完后,调用析构函数,引用计数都减1,都变成1,由于引用计数不为0,所以node1和node2所指向的对象不会被释放。
  • 当node1所指向的资源释放需要当node2中的_prev被销毁,就需要node2资源的释放,node2所指向的资源释放就需要当node1中的_next被销毁,就需要node1资源的释放。因此node1和node2都有对方的“把柄”,这两个就造成循环引用现象,最终这node1和node2资源就不会进行释放。
struct ListNode
{
	shared_ptr<ListNode> _next = nullptr;
	shared_ptr<ListNode> _prev = nullptr;
};

int main()
{
	// 循环引用
	shared_ptr<ListNode> node1(new ListNode);
	shared_ptr<ListNode> node2(new ListNode);
	//输出1
	cout << p1.use\_count() << endl;
	cout << p2.use\_count() << endl;

	node1->_next = node2;
	node2->_prev = node1;
	// use\_count(): 返回智能指针对象的引用计数
    //输出2
	cout << node1.use\_count() << endl;
	cout << node2.use\_count() << endl;

	return 0;
}

weak_ptr解决shared_ptr的循环引用

weak_ptr对象指向shared_ptr对象时,不会增加shared_ptr中的引用计数,因此当node1销毁掉时,则node1指向的空间就会被销毁掉,node2类似,所以weak_ptr指针可以很好解决循环引用的问题。

所以在定义双向链表或者在二叉树等有多个指针的时候,如果想要将该类型定义成智能指针,那么结构体内的指针需要定义成weak_ptr类型的指针,防止循环引用的出现。

weak_ptr不能单独管理资源,必须配合shared_ptr一块使用,解决shared_ptr中存在的 循环引用问题

struct ListNode
{
 //不支持用指针初始化,只能拷贝和shared\_ptr初始化
	weak_ptr<ListNode> _next ;
	weak_ptr<ListNode> _prev;
};

int main()
{
	// 循环引用
	shared_ptr<ListNode> node1(new ListNode);
	shared_ptr<ListNode> node2(new ListNode);
	//输出1
	cout << p1.use\_count() << endl;
	cout << p2.use\_count() << endl;

	node1->_next = node2;
	node2->_prev = node1;
	// use\_count(): 返回智能指针对象的引用计数
 //输出1
	cout << node1.use\_count() << endl;
	cout << node2.use\_count() << endl;

	return 0;
}

weak_ptr实现
// 不参与指向资源的释放管理
template<class T>
class weak\_ptr
{
public:
	weak\_ptr()
		:\_ptr(nullptr)
	{}
	//构造
	weak\_ptr(const shared_ptr<T>& sp)
		:\_ptr(sp.get())
	{}
	//拷贝构造
	weak_ptr<T>& operator=(const shared_ptr<T>& sp)
	{
		if (_ptr != sp.get())
		{
			_ptr = sp.get();
		}

		return \*this;
	}

	T& operator\*()
	{
		return \*_ptr;
	}

	T\* operator->()
	{
		return _ptr;
	}

public:
	T\* _ptr;
};

🏆5.定制删除器

出现问题

我们如果在动态内存中创建出一个数组,用一个unique_ptr对象去指向该数组,当unique_ptr使用完后,就会去调用析构函数,由于unique_ptr默认的删除方式是 delete ptr,后面没有带方括号,那么程序就会崩掉。

同理,如果我们打开一个了文件,返回一个文件指针,让一个unique_ptr对象去指向该文件,那么在调用析构函数的时候就不能采用delete方法,而是使用fclose()函数去关闭该文件

如果用malloc开辟了一段空间,那么也因该用free释放空间

class Date
{
public:
	~Date()
	{
		cout << "~Date()" << endl;
	}
private:
	int _year = 1;
	int _month = 1;
	int _day = 1;
};
int main ()
{
//下面程序会报错
	unique_ptr<Date> up1(new Date[10]);
	unique_ptr<Date,> up3((Date\*)malloc(sizeof(Date)\* 10));
	unique_ptr<FILE> up4((FILE\*)fopen("Test.cpp", "r"));
}

解决办法

下面是官方对unique_ptr定义,除了对应的指针外,还有一个带默认值的仿函数,所以对于上面情况,我们要写出对应的仿函数

template <class T, class D = default_delete<T>> class unique\_ptr;

下面是对unique_ptr改造(添加了默认定制删除器)和具体的实现过程

template<class T>
struct default\_delete
{
	void operator()(T\* ptr)
	{
		delete ptr;
	}
};

template<class T, class D = default_delete<T>>
class unique\_ptr
{
public:
	// RAII思想
	unique\_ptr(T\* ptr)
		:\_ptr(ptr)

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化的资料的朋友,可以添加V获取:vip204888 (备注大数据)
img

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

template

struct default_delete
{
void operator()(T* ptr)
{
delete ptr;
}
};

template<class T, class D = default_delete>
class unique_ptr
{
public:
// RAII思想
unique_ptr(T* ptr)
:_ptr(ptr)

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化的资料的朋友,可以添加V获取:vip204888 (备注大数据)
[外链图片转存中…(img-ygcC7woG-1713356181958)]

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

  • 23
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值