智能指针—强弱智能指针

智能指针通过自动管理内存,防止内存泄漏。本文聚焦于`shared_ptr`和`weak_ptr`,解释了它们如何解决内存管理和交叉引用问题。`shared_ptr`使用引用计数管理内存,当引用计数为零时自动释放。然而,对于交叉引用场景,`shared_ptr`会导致内存泄漏,此时`weak_ptr`作为观察者,不参与内存管理,可用于安全地解除循环引用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

智能指针通俗一点来说他是一个类,在类中有一个指针,可以将new获得的地址复制给智能指针。当智能指针过期时,类中的析构函数将使用delete来释放内存。

在平时编写代码的时候经常会用到new来开辟空间,而我们开辟出来的空间必须得手动去delete他,但是如果程序员忘记去手动释放那边会出现一个麻烦的问题,内存泄漏!!或者是一块内存被多个函数同时使用时,如果其中一个函数不知道还有其他人也在使用这块内存而释放掉的话同样也会引起程序的崩溃。
引起内存泄漏一般的后果是很严重的,而且也是最让人头疼的一个问题,根据上面的问题,由此产生了智能指针的引入。智能指针很好的解决了这种问题。指针指针又有很多种包括:std::auto_ptr、boost::scoped_ptr、boost::shared_ptr、boost::scoped_array、boost::shared_array、boost::weak_ptr、boost:: intrusive_ptr。而这篇文章主要用boost::shared_ptr和boost::weak_ptr这两种常用的智能指针来分析。

shared_ptr ,强智能指针,需要包含头文件头文件<memory>。他是专门用于共享所有权的,其在内部使用了引用计数。他是一种强引用,当有人需要用这个指针时他的引用计数都会加一,每析构一次引用计数就会减一,当引用计数为零时便会默认调用delete来释放也可以是自己的函数来释放。这样做的好处在于减轻了程序员手动释放内存的负担。之前,为了处理程序中的异常情况,往往需要将指针手动封装到类中,通过析构函数来释放动态分配的内存;现在这一过程就可以交由shared_ptr完成了。

shared_ptr 智能指针的实现

#include<iostream>
#include<map>
using namespace std;

template<typename T>
class smartptr
{
private:
	T *_ptr;
	static map<T*, int> _num;
public:
	smartptr(T *p)
	{
		_ptr = p;
		if (_num.find(p) != _num.end())
		{
			_num[p]++;
		}
		else
		{
			_num.insert(make_pair(p, 1));
		}
	}

	smartptr(const smartptr& src)
	{
		_ptr = _src._ptr;
		_num[src._ptr]++;
	}

	~smartptr()
	{
		if (--num[_ptr] == 0)
		{
			delete _ptr;
		}
	}

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

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

template<typename T>
map<T*, int> smartptr<T>::_num = map<T*, int>();

使用shared_ptr智能指针不仅可以解决内存泄漏的问题,巧妙的处理了当多个地方需要共享一块内存时这块内存的合理使用。因为他有引用计数的机制,这就能使他在多个地方同时使用这块内存时知道到底有几个人还在使用,当没有人在使用的时候他才会释放这块内存。不会有一些地方错误的释放掉了内存导致其他地方正常使用这块内存时出现错误。

 

虽然shared_ptr指针看似已经和完美了可以解决大部分的问题,但是还是有一个问题是shared_ptr指针不能解决的,那就是交叉引用的问题

class B;
class A
{
public:
	A()
	{
		cout << " A()" << endl;
	}
	~A()
	{
		cout << " ~A()" << endl;
	}
	shared_ptr<B> _ptrb;
};

class B
{
public:
	B()
	{
		cout << " B()" << endl;
	}
	~B()
	{
		cout << " ~B()" << endl;
	}
	shared_ptr<A> _ptra;
};
int main()
{
	shared_ptr<A> ptrA(new A());
	shared_ptr<B> ptrB(new B());

	ptrA->_ptrb = ptrB;
	ptrB->_ptra = ptrA;

	return 0;
}

在这里强智能指针ptrA指向对象A,而B类中有强智能指针_ptrb指向A,则ptrA的引用计数为二,同理强智能指针ptrB指向对象B,而A类中有强智能指针_ptra指向B,则ptrB的引用计数为二

上面的程序运行结果为:

这里并没用调用两个类中的析构函数,因为这两个智能指针的引用计数开始时为二,当主函数结束时两个智能指针的引用计数都会减一,但是并没有到零所以并不会调用析构函数。这里便会导致内存没有被释放,从而导致内存泄漏。

根据这种问题便出现了弱智能指针的引入,

weak_ptr是为了配合shared_ptr而引入的一种智能指针,它更像是shared_ptr的一个助手而不是智能指针,因为它不具有普通指针的行为,没有重载operator*和->,它的最大作用在于协助shared_ptr工作,像旁观者那样观测资源的使用情况.

weak_ptr被设计为与shared_ptr共同工作,可以从一个shared_ptr或者另一个weak_ptr对象构造,获得资源的观测权。但weak_ptr没有共享资源,它的构造不会引起指针引用计数的增加。这种弱引用它并不对对象的内存进行管理,在功能上类似于普通指针。当shared_ptr析构时不用管weak_ptr是否引用了,所以weak_ptr指向的内存并不能确保是有效的,在使用时首先要判断weak_ptr是否有效。

在引入weak_ptr后上面的程序可以这样改,采用强弱指针并用的方式来解决问题。

class B;
class A
{
public:
	A()
	{
		cout << " A()" << endl;
	}
	~A()
	{
		cout << " ~A()" << endl;
	}
	weak_ptr<B> _ptrb;
};

class B
{
public:
	B()
	{
		cout << " B()" << endl;
	}
	~B()
	{
		cout << " ~B()" << endl;
	}
	weak_ptr<A> _ptra;
};
int main()
{
	shared_ptr<A> ptrA(new A());
	shared_ptr<B> ptrB(new B());

	ptrA->_ptrb = ptrB;
	ptrB->_ptra = ptrA;

	return 0;
}

还是之前的那个问题,只不过把类中的强指针shared_ptr改为弱指针weak_ptr。
运行的结果为:

经过上面的改动A和B的引用计数都会为一,当程序结束时两个的引用计数减一变为零,这样便会调用析构函数。

最后在强若指针使用时为了避免智能指针循环引用造成的内存泄露情况,一定要创建对象用强智能指针,其他地方只能用持有资源的弱智能指针。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值