C++ 智能指针shared_ptr

C++智能指针shared_ptr概述

在介绍智能指针前先看如下代码

void func1(std::string& str)
{
	std::string* ps = new std::string(str);
	str = *ps;
	return;
}
void func2(std::string& str)
{
	std::string* ps = new std::string(str);
	if (weird_thing())
		throw ecception();
	str = *ps;
	delete ps;
	return;
}

对于func1函数, 由于我们忘记了delete,该函数的每一次的调用,都会在堆区中开辟内存,但从不收回,从而导致内存泄漏.对于func2我们没有忘记delete,但当程序出现异常的时候,会从if语句处结束函数调用,下面的delete并没有执行,从而导致内存泄漏.程序总会在我们粗心大意的时候遇到一些内存的问题.因为ps指针是一个常规的指针,所以我们很难对其管理,如果他是一个对象,那么在对象过期的时候他就会自动销毁.所以这引入了智能指针.

智能指针概述:
C++中引入智能指针(smart pointer)的主要目的是为了管理动态内存块,以确保内存块的安全和高效。智能指针是一种特殊的指针类型,它可以在内存块被释放之前自动释放内存块,从而避免了手动释放内存块的繁琐和耗时。

shared_ptr使用方法

  • shared_ptr介绍:

C++中的std::shared_ptr是一种智能指针类型,用于管理动态内存块。如果要创建智能指针对象,需要包含头文件memory,c++中智能指针有四种分别是auto_ptr,shared_ptr,unique_ptr,weak_ptr这里首先介绍shared_ptr.

  • shared_ptr定义方式
shared_ptr<类型> 指针名字;
  • 案例
	shared_ptr<double> pd; // 定义了一个空智能指针
	shared_ptr<int> pi(new int(100)); // pi指向一个4字节大小的地址块,存放值为100;
	double* p_reg = new double;

	// 对于智能指针都有一个explicit构造函数, 可以将常规指针作为参数传递给智能指针, 但不推荐这么做,可能会引发问题.
	pd = shared_ptr<double>(p_reg); // 允许强制转换
	shared_ptr<double> pd_1 = p_reg; // 不能将常规指针隐式转换为智能指针, 只允许强制转换
	shared_ptr<double> pd_2(p_reg); // 可以将常规指针作为参数传递给智能指针

shared_ptr引用计数的增加与减少

  1. shared_ptr的引用计数
    对于shared_ptr它会记录有多少个其他的shared_ptr指向相同的对象,当指向它的对象过期时, 如果只有一个指向它的指针,则会将指针指向的内存销毁,如果不是最后一个,他会将过期的指针对象置空
	shared_ptr<double> pd1 = make_shared<double>(100.34); // 在调试过程中,将鼠标悬停到pd1上会出现1 strong, 代表一个强引用,
	auto pd2 = pd1; // 同上所述, 当悬停时会出现2 strong代表2个强引用.

std::make_shared函数可以将一个对象包装成智能指针.它返回一个指向该对象的std::shared_ptr对象.

  1. 引用计数的增加
    如下情况会增加shared_ptr的引用计数
    a)像上面一样用一个智能指针对象初始化另外一个智能指针对象
    b)把智能指针用传值的方式进行函数参数的传递
    c)作为函数的返回值

  2. 引用计数的减少
    如下情况会减少shared_ptr的引用计数减少
    a)给shared_ptr赋予新值,指向别的内存,会导致shared_ptr引用计数减少
    b)局部的shared_ptr离开其作用域
    c)当一个shared_ptr从1变成零,它会释放其指向的对象

	shared_ptr<double> pd1 = make_shared<double>(100.34);
	shared_ptr<double> pd2 = pd1;
	shared_ptr<double> pd3(new double(300.3));
	pd1 = pd3;
	// pd1的指向改变, 指向的引用对象的计数会增加, 但原先的对象由于计数从1变成零所以原来的对象会被销毁.

shared_ptr常用操作

  1. use_count(): 返回某个对象的智能指针数
	shared_ptr<int> a(new int);
	a.use_count(); // 返回1;

	shared_ptr<int> b(a);
	b.use_count(); a.use_count(); // 返回2;
  1. unique(): 某对象的智能指针数为1返回true否则返回false
	shared_ptr<int> a(new int);
	a.unique(); // 返回true;

	shared_ptr<int> b(a);
	a.unique(); // (b.unique()) 返回false;
  1. reset():
    a)reset参数为空
    若指针是唯一指向该对象的, 则释放指针指向的对象,将指针置空,若指针不是唯一的,不会释放指向的对象, 但会将指针置空, 计数减一
	shared_ptr<int> p1(new int(400));
	auto p2(p1);
	p1.reset(); // 将p1置空, 引用计数减一
	p2.reset(); // 释放指向的对象, 将指针置空

b)reset带参数时(一般都是new 出来一个新的指针)
若指针指向的对象唯一, 释放指针指向的对象, 指向新对象.若指针指向的对象不唯一,则不会释放指向的对象, 但会将指针指向为新的对象,计数减一

	shared_ptr<int> p1(new int(400));
	auto p2(p1);
	p1.reset(new int); // 不会释放指向的对象, 指向新的对象
	p2.reset(new int); // 释放指向的对象, 指向新的对象
  1. 解引用: 获得指向的对象
shared_ptr<int> a(new int(100));
cout << *a << endl; // 打印100;
  1. get(): 返回指针中保存的指针(裸指针)
    裸指针是指,用new直接开辟的指针(如p = new int, p1 = new int(200))
	shared_ptr<int> p(new int(100));
	int* pi = p.get();
	cout << *pi << endl;
	注意: 不能释放pi(delete pi), 否则会导致不可预料的后果
  1. swap(): 交换两个智能指针指向的对象
	shared_ptr <string> p1(new string("C++"));
	shared_ptr<string> p2(new string("Java"));
	swap(p1, p2); or p1.swap(p2);
  1. =nulllptr :将所指向的对象, 引用计数减一, 若引用计数为零, 释放指向的对象.
	shared_ptr<int> p(new int(100));
	p = nullptr;
  1. 智能指针判断: 可以将智能指针作为判断条件
	shared_ptr<int> p(new int(100));
	p = nullptr;

	if (p) cout << "p指向一个对象" << endl;
	else cout << "没有指向对象" << endl;
  1. 指定删除器以及数组问题
    a)智能指针里面保存了一个裸指针, 在对象过期时会自动析构(析构方式为delete p(保存的裸指针)), 我们可以自己提供删除器,当我们指定了自己的删除器, 编译器会自动选择我们的删除器.将删除器作为参数名,即可调用自己的删除器
void My_Del(int* p)
{
	cout << "哈哈" << endl;
	delete p;
}
shared_ptr<int> p(new int(100),My_Del);
p.reset(); // 释放对象, 打印哈哈

b) 删除器也可以是一个lambda表达式

shared_ptr<int> p(new int(100), [](int* p) { delete p; });

c)删除器不能删除动态数组(如new int[10]), 原因是,默认的删除器删除的是指针所指向的裸指针(delete p) 而不是(delete [] p), 所以在这种情况下我们必须要提供自己的删除器

shared_ptr<int> p(new int[100], [](int* p) { delete []p; });

除了定义函数和lambda表达式指定为删除器,也可以使用default_delete来做删除器, default_delete是标准库里面的模板类

shared_ptr<Type> p(new Type[100, default_delete<Type[]>());

在定义数组的时候可以直接添加[]就可以实现删除器

shared_ptr<int[]> p(new int[100]);

下面介绍一种封装智能指针数组的例子,以后会经常遇见

template<typename T>
shared_ptr<T> make_shared_arry(size_t size)
{
	return shared_ptr<T>(new T[size], std::default_delete<T[]>());
}

shared_ptr<int> pArr = make_shared_arry<int>(5); // 返回一个数组

d)指定删除器的额外说明
就算是两个shared_ptr指定了不同的删除器, 只要他们所指向的对象类型相同, 那么这两个shared_ptr也属于同一个类型.

	auto lambda1 = [](int* p) { delete p; };
	auto lambda2 = [](int* p) { delete p; };

	shared_ptr<int> p1(new int(100), lambda1); // 指定删除器为1
	shared_ptr<int> p2(new int(200), lambda2); // 指定删除器为2;

	p2 = p1; // p2会先调用2, 释放指向的对象, 然后指向p1所指向的对像, p1计数增加为2
	//当main()执行完毕后, 会调用1来释放,p1, p2, 虽然p2的删除器为2, 所以他们是同一类型, 所以调用了同一删除器.
	vector<shared_ptr<int> num(p1, p2) // 类型相同, 可以放到同一个容器

补充: 对于make_shared能够生成shared_ptr, 但是使用了make_shared之后就不能指定自己的删除器

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值