C++基础(十)智能指针之shared_ptr

为了一定程度解决手动分配的动态内存忘记释放的问题,C++引入了智能指针的概念。并且在C++11中,将智能指针进行了优化,更加方便易用。

智能指针,顾名思义,实质还是指针,不过它很智能,会在合适的时机自己释放内存,无需手动delete。其原理就是引入计数,一旦指向某一块内存的指针数为0,则释放。

常用的智能指针为共享指针shared_ptr独占指针unique_ptr,共享指针可以多个智能指针指向同一块内存;而独占指针则在某个时刻只能有一个智能指针指向某一块内存。

用智能指针,需要包含头文件:#include <memory>。

这篇文章仅介绍共享智能指针shared_ptr的使用及注意事项,另一篇文章介绍独占智能指针unique_ptr的知识

1、创建及初始化

共享指针的创建,最安全的做法是使用标准库的std::make_shared函数,具体语法如下:

std::shared_ptr<类型>(参数列表)

智能指针是一个模板。

其中,类型即需要创建的智能指针类型,比如int、double或者自定义的类;参数列表可以理解成类的构造函数需要的参数,可以为空,但小括号必须要。

例如,自定义Person类,如下:

class Person
{
public:
	Person(const std::string& strName, int iAge) :m_strName(strName), m_iAge(iAge){}
	~Person(){ std::cout << "Person析构, name=" << m_strName << std::endl; }

	void PrintInfo(){ std::cout << "姓名:" << m_strName << ", 年龄:" << m_iAge << std::endl; }
private:
	std::string m_strName;
	int m_iAge;
};

在主函数中创建一个Person类以及int类型的shared_ptr并使用,代码如下:

int _tmain(int argc, _TCHAR* argv[])
{
	auto p2 = std::make_shared<int>(25); //初始化为25,并且用auto来定义对象
	std::cout << "pInt2:" << *p2 << std::endl;

	std::shared_ptr<int> pInt1; //定义后并未立即初始化
	pInt1 = std::make_shared<int>(); //这里参数列表为空,int会默认初始化为0
	std::cout << "pInt1:" << *pInt1 << std::endl;

	//自定义类
	std::shared_ptr<Person> pZhangFei = std::make_shared<Person>("张飞", 42);
	pZhangFei->PrintInfo();

	system("pause");
	return 0;
}

执行结果如下:

 

2、拷贝、赋值及自动释放内存

添加一个根据名字和年龄,获取相应智能指针的接口,如下:

std::shared_ptr<Person> GetPerson(const std::string strName, int iAge)
{
	return std::make_shared<Person>(strName, iAge);
}

主函数修改如下:

int _tmain(int argc, _TCHAR* argv[])
{
	{
		std::shared_ptr<Person> p1 = GetPerson("赵云", 40);
		{
			std::shared_ptr<Person> p2 = GetPerson("关羽", 48);
			p2->PrintInfo();

			p1 = p2; //此时原p1指向的内存对象会被释放掉,p1与p2指向同一个对象
		}
		p1->PrintInfo(); 
	}

	system("pause");
	return 0;
}

执行结果:

主函数中,p1初始化的是赵云,接下来一对大括号,p2被初始化为关羽,打印p2,得到的是关羽的信息;接下来,将p2赋值给p1,此时,p1原本指向的内存对象赵云,没有任何指针指向它,计数变为了0,所以会自动释放其内存,调用析构函数,打印释放赵云对象的信息。赋值结束后,p2的作用域结束,但是因为p1已经指向了对象关羽,所以关于对象的指针计数不为0,内存并不会被释放;打印p1的信息,依然是关羽。接下来遇到大括号,p1的作用域失效,指向关羽的指针计数变成0,内存会被自动释放,打印了释放对象关羽的信息。

shared_ptr指向的内存对象,只有当指向它的智能指针引用计数为0时,才会自动释放。

理解这几行代码,基本上就可以理解共享指针自动释放内存的原理。

3、注意事项

(1)不要混用普通指针和智能指针

谨记一点,不要将普通指针赋值给智能指针,否则可能会引发很严重的后果。

请看下面代码:


void Process(std::shared_ptr<int> p){}

int _tmain(int argc, _TCHAR* argv[])
{
	int* p1 = new int(20);
	std::cout << *p1 << std::endl;
	Process(std::shared_ptr<int>(p1));
	if (p1)
		std::cout << *p1 << std::endl;

	system("pause");
	return 0;
}

代码很简单,定义了一个什么都不做的Process函数,接收一个shared_ptr<int>类型的智能指针作为参数。

主函数里,new一个普通指针p1,打印p1指向的对象内容;再调用Process函数并以p1作为参数构建一个临时的智能指针作为参数传递,再打印p1。

结果会是什么呢?

有些出乎意料,为何会如此?

第一次打印出来20,很简单,就是p1指向的内存内容。第二次打印的是未定义的内容,说明p1成了一个空悬指针。

问题出在Process的调用上。我们先用p1作为参数,构建了一个临时的share_ptr指针,这没问题;但是这个临时的智能指针指向的是p1的内存对象,当Process执行结束,这个临时的智能指针作用域消失,其指向的内存引用计数变成0,会自动释放内存,即p1指向的内存。此时p1依然有效,但是它的内存已经被释放,p1就成了一个空悬指针(注意,用if(p1)来判断p1仍然是有效的)。

(2)慎用get

可用get获取智能指针中的指针,但一定要注意,千万不要误delete这个普通指针。

看如下代码:

int _tmain(int argc, _TCHAR* argv[])
{
	std::shared_ptr<int> pShare(new int(7));
	std::cout << *pShare << std::endl;
	int *p = pShare.get();
	delete p;
	if (pShare)
		std::cout << *pShare << std::endl;

	system("pause");
	return 0;
}

执行结果如下:

这里用new 的方式初始化了一个shared_ptr指针,打印内容为7,没问题。

接着,调用shared_ptr的接口get,获取其内部的指针,赋值给普通指针p;紧接着,delete p,将普通指针释放。此时,pShare的指向的内存也被释放了,用if判断,pShare本身依然有效;尝试打印其中内容,未定义。

共享指针的使用比较简单,就介绍这么多。

 

 

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值