C++ - 智能指针

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

内存泄漏的例子:

C++中内存泄漏的几种情况_Denny_Zoom的博客-CSDN博客_内存泄漏的几种情况

C++造成内存泄漏的原因汇总:_盖伊福克斯的博客-CSDN博客_c++内存泄漏

c++内存泄露(一):理解内存泄漏及内存泄漏常见情况_invisible_sky的博客-CSDN博客_c++ 内存泄露

一 auto_ptr

auto_ptr以前是用在C98中,C++11被抛弃,头文件一般用来作为独占指针

auto_ptr被赋值或者拷贝后,失去对原指针的管理

auto_ptr不能管理数组指针,因为auto_ptr的内部实现中,析构函数中删除对象使用delete而不是delete[],释放内存的时候仅释放了数组的第一个元素的空间,会造成内存泄漏。

auto_ptr不能作为容器对象,因为STL容器中的元素经常要支持拷贝,赋值等操作。

代码示例:

// Weak_Ptr_Use.cpp : 定义控制台应用程序的入口点。
//
#include "stdafx.h"
#include <iostream>
#include <memory>
using namespace std;

class A
{
public:
	A(){
		cout << "A Init" << endl;
	}
	~A()
	{
		cout << "A Destory" << endl;
	}
	void show()
	{
		cout << "demo" << endl;
	}
};

void test_auto_demo()
{
	unique_ptr<A> mma(new A());
	cout << mma << endl;
	auto_ptr<A> A_PTR(new A());
	auto_ptr<A>mm = A_PTR; //实质和std::move()语义一样。
	cout << mm.get() << endl;
	cout << A_PTR.get() << endl;
	A_PTR->show();
}

int main()
{
	test_auto_demo();
	system("pause");
	return 0;
}

运行结果:

在这里插入图片描述

造成在这个的原因是auto_ptr<A>mm = A_PTR; //实质和std::move()语义一样。
这边A_PTR已经把所有权转给mm,此时A_PTR为空指针,空指针必然报错。。
所以unique_ptr比auto_ptr更加安全,实质上个人觉得应该是编译器对unique_ptr的处理更加灵活。
 

二 unique_ptr

C++11中用来替代auto_ptr

拷贝构造和赋值运算符被禁用,不能进行拷贝构造和赋值运算

虽然禁用了拷贝构造和赋值运算符,但unique_ptr可以作为返回值,用于从某个函数中返回动态申请内存的所有权,本质上是移动拷贝,就是使用std:move()函数,将所有权转移。

代码示例:

class A{
public:
	A(){
		cout << "demo"<< endl;
	}
  void B(){
		cout << "测试" << endl;
	}
};

void unique_ptr_use()
{
	unique_ptr<A> A_ptr(new A());
	A_ptr->B();
	cout << A_ptr << endl;
	unique_ptr<A> demo = A_ptr; //此时编译错误,无法通过赋值的方式传递指针地址,即与demo共享同一块内存
}

由上面代码可以看出,编译unique_ptr<A> demo = A_ptr会报错,因为unique_ptr是只能单独享有对对象的独有权。
另外,unique_ptr无法通过赋值的形式创建内存对象,即:

在这里插入图片描述

这种方式也是错误的。

提前释放unique_ptr对象可以通过reset方法进行重置

代码示例:

void unique_ptr_use()
{
	unique_ptr<A> A_ptr(new A());
	A_ptr->B();
	cout << A_ptr << endl;
	A_ptr.reset();
	cout << A_ptr << endl;
}

 运行结果:

在这里插入图片描述

 可以看出:
通过reset方法 可以直接删除原始指针,并且重置为空。

我们可以通过转移所有权的方式,来使另一个指针拥有原先unique_ptr的地址。

代码示例:

void unique_ptr_use()
{
	unique_ptr<A> A_ptr(new A());
	A_ptr->B();
	cout << A_ptr << endl;
	unique_ptr<A>BB = std::move(A_ptr);
	cout << A_ptr << endl;
	cout << BB << endl;
}

运行结果:

在这里插入图片描述

通过std::move()方法,将A_ptr的对象转移到BB上面,A_ptr指针为空,BB拥有A_ptr指向的地址。

三 shared_ptr 

多个指针可以指向相同的对象,调用release()计数-1,计数0时资源释放

use_count()查计数

reset()放弃内部所有权

share_ptr多次引用同一数据会导致内存多次释放

循环引用会导致死锁,

引用计数不是原子操作

shared_ptr可以实现多个对象共同托管一个指针,这个是unique_ptr做不到的。当曾经的托管对象接触对其托管时,系统会自动执行delete p释放内存,这样保证了内存不会泄漏。

代码示例:

void shared_ptr_use()
{
	shared_ptr<A> mm = make_shared<A>();
	mm->B();
	cout <<"mm value:" << mm << endl;
	shared_ptr<A> ff(mm);
	cout << "ff value:" << ff << endl;
	cout << "个数为" << ff.use_count() << endl;
	mm.reset();//重置
	cout <<"now ff:"<< ff << endl;
	cout << "个数为" << ff.use_count() << endl;
	cout <<"now mm:"<< mm << endl;
}

运行结果:

在这里插入图片描述

四 weak_ptr 

1.解决两个share_ptr互相引用产生死锁,计数永远降不到0,没办法进行资源释放,造成内存泄漏的问题。

2.使用时配合share_ptr使用,把其中一个share_ptr更换为weak_ptr

代码示例:

class B;
class A{
public:
	A(){
		cout << "A init!" << endl;
	}
	~A(){
		cout << "A Destroy" << endl;
	}
	void set_quote_ptr(shared_ptr<B>mm)
	{
		m_b = mm;
	}
private:
	shared_ptr<B> m_b;
};

class B{
public:
	B(){
		cout << "A init!" << endl;
	}
	~B(){
		cout << "A Destroy" << endl;
	}
	void set_quote_ptr(shared_ptr<A>mm)
	{
		m_a = mm;
	}
private:
	shared_ptr<A> m_a;
};

void test_ptr()
{
	shared_ptr<A>a_ptr = make_shared<A>();
	shared_ptr<B>b_ptr = make_shared<B>();
	a_ptr->set_quote_ptr(b_ptr);
	b_ptr->set_quote_ptr(a_ptr);
	cout << "a count" << a_ptr.use_count() << endl;
	cout << "b count" << b_ptr.use_count() << endl;
}

int main()
{
	test_ptr();
	system("pause");
	return 0;
}

此时他们之前存在相互引用,但是已经方法已经返回了还没稀释
运行结果:

在这里插入图片描述

解释原因可以参考:智能指针(三):weak_ptr浅析_AlbertS的博客-CSDN博客_weakptr

解释了无法析构的原因,这里简单介绍一下。主要是相互引用时,导致引用记数无法为0;所以无法调用函数进行析构。

接下来解决的方案,是使用weak_ptr来替代其中一个shared_ptr,因为这样赋值的话,不会引发计数变化,修改完的代码如下:

#include "stdafx.h"
#include <iostream>
#include <memory>
using namespace std;

class B;
class A{
public:
	A(){
		cout << "A init!" << endl;
	}
	~A(){
		cout << "A Destroy" << endl;
	}
	void set_quote_ptr(shared_ptr<B>mm)
	{
		m_b = mm;
	}
private:
	weak_ptr<B> m_b;
};

class B{
public:
	B(){
		cout << "A init!" << endl;
	}
	~B(){
		cout << "A Destroy" << endl;
	}
	void set_quote_ptr(shared_ptr<A>mm)
	{
		m_a = mm;
	}
private:
	weak_ptr<A> m_a;
};

void test_ptr()
{
	shared_ptr<A>a_ptr = make_shared<A>();
	shared_ptr<B>b_ptr = make_shared<B>();
	a_ptr->set_quote_ptr(b_ptr);
	b_ptr->set_quote_ptr(a_ptr);
	cout << "a count" << a_ptr.use_count() << endl;
	cout << "b count" << b_ptr.use_count() << endl;
}

int main()
{
	test_ptr();
	system("pause");
	return 0;
}

运行结果:

在这里插入图片描述

可以明显的看出,已经被析构了。 


 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

嵌入式_笔记

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

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

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

打赏作者

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

抵扣说明:

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

余额充值