C++智能指针

C++智能指针

在C++中堆内存申请和释放是非常频繁的操作,他们都是由程序员自己管理的,内存的管理是用一对运算符完成的:new和delete,new:在动态内存中为对象分配一块空间并返回一个指向该对象的指针,delete:指向一个动态独享的指针,销毁对象,并释放与之关联的内存。但是何时释放资源呢?就是在最后一个引用它的对象被释放的时候释放它。关键的问题在于无法确定哪个引用它的对象是被最后释放的。
C++11中引入了智能指针的概念,方便管理堆内存。使用普通指针,容易造成堆内存泄露(忘记释放),二次释放,程序发生异常时内存泄露等问题,使用智能指针能更好的管理堆内存。std::shared_ptr确定最后一个引用它的对象何时被释放的基本想法是:对被管理的资源进行引用计数。unique_ptr则“独占”所指向的对象。标准库还定义了一种名为weak_ptr的伴随类,它是一种弱引用,指向shared_ptr所管理的对象,这三种智能指针都定义在memory头文件中。

  1. shared_ptr

当一个shared_ptr对象要共享这个资源的时候,该资源的引用计数加1,当这个对象生命期结束的时候,再把该引用技术减少1。这样当最后一个引用它的对象被释放的时候,资源的引用计数减少到0,此时释放该资源。
用法

	int b = 20; 
	std::shared_ptr<int> ptrb = std::make_shared<int>(b);
	std::shared_ptr<int> ptrd(ptrb);
	std::shared_ptr<int> ptre(ptrb);
	int* nshared_ptr = ptrb.get();
	std::shared_ptr<int> ptrc = std::make_shared<int>(b);
	std::cout << ptrb.use_count() << std::endl;
	std::cout << ptrc.use_count() << std::endl;
	ptrc.swap(ptrb);//交换两个指针的值,交换后引用计数也交换
	std::cout << ptrb.use_count() << std::endl;
	std::cout << ptrc.use_count() << std::endl;
	ptrc = ptrb;//使用=赋值后ptrb的计数加一,ptrc的计数变成ptrb加一以后的个数
	std::cout << ptrb.use_count() << std::endl;
	std::cout << ptrc.use_count() << std::endl;
	//可以使用reset来将一个新的指针赋予一个shared_ptr:,
	//reset中如果没有参数且对象没有初始化应用次数为0,初始化了则减1
	//如果有参数且对象没有初始化则加一
	std::shared_ptr<int> ptrf;
	ptrf.reset();
	//ptrf = new int(1024);//错误:不能将一个指针赋予shared_ptr
	std::cout <<"ptrf" << ptrf.use_count() << std::endl;
	ptrf.reset(new int(1024));//正确。p指向一个新对象
	std::cout << "ptrf" << ptrf.use_count() << std::endl;

注意事项
不要混合使用普通指针和智能指针
如果混合使用的话,智能指针自动释放之后,普通指针有时就会变成悬空指针,当将一个shared_ptr绑定到一个普通指针时,我们就将内存的管理责任交给了这个shared_ptr。一旦这样做了,我们就不应该再使用内置指针来访问shared_ptr所指向的内存了。也不要使用get初始化另一个智能指针或为智能指针赋值。

	std::shared_ptr<int> pshared =std::make_shared<int>(555);//引用计数为1
	//p11 = new int(1024);//错误:不能将一个指针赋予shared_ptr
	pshared.reset(new int(1024));//正确。p指向一个新对象
	int* qint = pshared.get();//正确:但使用q时要注意,不要让它管理的指针被释放
	 {
		       //新程序块
		       //未定义:两个独立的share_ptr指向相同的内存
		       delete qint;//这样会崩溃
		
	}//程序块结束,q被销毁,它指向的内存被释放
	int foo = *pshared;//未定义,p指向的内存已经被释放了

智能指针和异常
如果使用智能指针,即使程序块过早结束,智能指针也能确保在内存不再需要时将其释放,sp是一个shared_ptr,因此sp销毁时会检测引用计数,当发生异常时,我们直接管理的内存是不会自动释放的。如果使用内置指针管理内存,且在new之后在对应的delete之前发生了异常,则内存不会被释放。

  1. unique_ptr

unique_ptr“唯一”拥有其所指对象,同一时刻只能有一个unique_ptr指向给定对象(通过禁止拷贝语义、只有移动语义来实现)。相比与原始指针unique_ptr用于其RAII的特性,使得在出现异常的情况下,动态资源能得到释放。unique_ptr指针本身的生命周期:从unique_ptr指针创建时开始,直到离开作用域。离开作用域时,若其指向对象,则将其所指对象销毁(默认使用delete操作符,用户可指定其他操作)。unique_ptr指针与其所指对象的关系:在智能指针生命周期内,可以改变智能指针所指对象,如创建智能指针时通过构造函数指定、通过reset方法重新指定、通过release方法释放所有权、通过移动语义转移所有权。
用法

	std::unique_ptr<std::string> ptrstring(new std::string("Trex"));
	// std::unique_ptr<std::string> ptrstring1(ptrstring);不可以
	std::unique_ptr<std::string> p2111(ptrstring.release());//将ptrstring置为空
    std::cout << ptrstring << std::endl;//可以使用为空 
	std::unique_ptr<std::string>p3(new std::string("Trex")); 
	std::unique_ptr<std::string> uptr2 = std::move(p2111); //转换所有权
	//将所有权从p3转移到p2
	p2111.reset(p3.release());//reset释放了p2原来指向的内存 
  1. weak_ptr

weak_ptr是为了配合shared_ptr而引入的一种智能指针,因为它不具有普通指针的行为,没有重载operator*和->,它的最大作用在于协助shared_ptr工作,像旁观者那样观测资源的使用情况。weak_ptr可以从一个shared_ptr或者另一个weak_ptr对象构造,获得资源的观测权。但weak_ptr没有共享资源,它的构造不会引起指针引用计数的增加。使用weak_ptr的成员函数use_count()可以观测资源的引用计数,另一个成员函数expired()==true的功能等价于use_count()==0,但expired更快,表示被观测的资源(也就是shared_ptr的管理的资源)已经不复存在,也就是说lock()函数将返回一个存储空指针的shared_ptr。
用法

	std::shared_ptr<int> sh_ptr = std::make_shared<int>(10);
	std::cout << sh_ptr.use_count() << std::endl;
	
	std::weak_ptr<int> wp(sh_ptr);
	std::cout << wp.use_count() << std::endl;
	
	if (!wp.expired()) {//查看是否有值
		std::shared_ptr<int> sh_ptr2 = wp.lock(); //获得一个shared_ptr
		*sh_ptr = 100;
		std::cout << wp.use_count() << std::endl;
	}
  1. 解决循环引用问题

在Man类内部会引用一个Woman,Woman类内部也引用一个Man。当一个man和一个woman是夫妻的时候,他们直接就存在了相互引用问题。man内部有个用于管理wife生命期的shared_ptr变量,也就是说wife必定是在husband去世之后才能去世。同样的,woman内部也有一个管理husband生命期的shared_ptr变量,也就是说husband必须在wife去世之后才能去世。这就是循环引用存在的问题:husband的生命期由wife的生命期决定,wife的生命期由husband的生命期决定,最后两人都死不掉,违反了自然规律,导致了内存泄漏。
解决std::shared_ptr循环引用问题的钥匙在weak_ptr手上。weak_ptr对象引用资源时不会增加引用计数,但是它能够通过lock()方法来判断它所管理的资源是否被释放。另外很自然地一个问题是:既然weak_ptr不增加资源的引用计数,那么在使用weak_ptr对象的时候,资源被突然释放了怎么办呢?呵呵,答案是你根本不能直接通过weak_ptr来访问资源。那么如何通过weak_ptr来间接访问资源呢?答案是:在需要访问资源的时候weak_ptr为你生成一个shared_ptr,shared_ptr能够保证在shared_ptr没有被释放之前,其所管理的资源是不会被释放的。创建shared_ptr的方法就是lock()方法。
示例代码

	#include <iostream>
	#include <memory>
	 
	class Woman;
	class Man{
	private:
		std::weak_ptr<Woman> _wife;
		//std::shared_ptr<Woman> _wife;
	public:
		void setWife(std::shared_ptr<Woman> woman){
			_wife = woman;
		}
	 
		void doSomthing(){
			if(_wife.lock()){
			}
		}
	 
		~Man(){
			std::cout << "kill man\n";
		}
	};
	 
	class Woman{
	private:
		//std::weak_ptr<Man> _husband;
		std::shared_ptr<Man> _husband;
	public:
		void setHusband(std::shared_ptr<Man> man){
			_husband = man;
		}
		~Woman(){
			std::cout <<"kill woman\n";
		}
	};
	 
	 
	int main(int argc, char** argv){
		std::shared_ptr<Man> m(new Man());
		std::shared_ptr<Woman> w(new Woman());
		if(m && w) {
			m->setWife(w);
			w->setHusband(m);
		}
		return 0;
	}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值