智能指针浅析

智能指针是为了解决C++中手动内存管理可能导致的内存泄漏问题。文章介绍了智能指针的原理,包括Auto_ptr的管理权转移、Boost库中的scoped_ptr和shared_ptr,以及如何使用weak_ptr解决循环引用问题。通过示例代码,详细阐述了智能指针的工作机制和使用注意事项。

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

                                 智能指针

一、什么是智能指针?

  资源分配即初始化,定义一个类来封装资源的分配和释放,在构造函数完成资源的分配和初始化,在析构函数完成资源的清理,可以保证资源的正确初始化和释放。

  对于编译器来说,智能指针实际上是一个栈对象,并非指针类型,在栈对象生命期即将结束时,智能指针通过析构函数释放有它管理的堆内存。所有智能指针都重载了“operator->”操作符,直接返回对象的引用,用以操作对象。访问智能指针原来的方法则使用“.”操作符。

二、为什么要有智能指针的存在?

  由于C++语言没有内存的自动回收机制,程序员每次new出来的内存都要手动delete释放,而当程序员忘delete,或者流程复杂,异常导致没有运行到delete提前退出,就会造成内存泄漏,防不胜防,则用智能指针可以有效缓解这些问题。

三、智能指针的发展

         1.Auto_ptr 的管理权转移(了解机制,不建议使用)

用一段代码配合解析

template<class T>
class AutoPtr
{
public:
	AutoPtr(T* ptr)
		:_ptr(ptr)
	{}

	~AutoPtr()
	{
		if (_ptr)
		{
			printf("delete:0x%p\n", _ptr);
			delete _ptr;
		}
	}

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

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

	// ap2(ap1)
	AutoPtr(AutoPtr<T>& ap)
		:_ptr(ap._ptr)
	{
		ap._ptr = NULL;
	}

	// ap2 = ap3
	AutoPtr<T>& operator=(AutoPtr<T>& ap)
	{
		if (this != &ap)
		{
			if (_ptr)
			{
				printf("delete:0x%p\n", _ptr);
				delete _ptr;
			}

			_ptr = ap._ptr;
			ap._ptr = NULL;
		}

		return *this;
	}

protected:
	T* _ptr;
};

void TestAutoPtr()
{
	int* p = new int(10);
	AutoPtr<int> ap1(p);
	AutoPtr<int> ap2(ap1);

	AutoPtr<int> ap3(ap2);

	AutoPtr<int> ap4(new int(20));
	ap3 = ap4;
}

管理权转移的方法会导致有些对象悬空,解引用它们就会使程序崩溃。例如上面代码中

这块空间的管理权从ap1转给ap2,ap1则指向空,因此不能再使用,同理代码中的p,ap2,ap4都不能再使用。

   2.Boost库的智能指针(ps:新的C++11标准中已经引入了unique_ptr/shared_ptr/weak_ptr)


(1)scoped_ptr/scoped_array 防拷贝(只声明不定义,声明成私有)

模拟实现scoped_ptr

template<class T>
class ScopedPtr
{
public:
	// RAII
	ScopedPtr(T* ptr)
		:_ptr(ptr)
	{}

	~ScopedPtr()
	{
		printf("delete:0x%p\n", _ptr);
		delete _ptr;
	}

	// 像指针一样--智能指针的特性
	T& operator*()
	{
		return *_ptr;
	}

	T* operator->()
	{
		return _ptr
	}

// 防拷贝
	// 1.只声明不实现
	// 2.声明成私有保护
private:
	ScopedPtr(const ScopedPtr<T>&);
	ScopedPtr<T>& operator=(const ScopedPtr<T>&);

protected:
	T* _ptr;
};

void TestScopedPtr()
{
	ScopedPtr<int> sp1(new int(10));
	ScopedPtr<int> sp2(sp1);
}

ScopedPtr对成员函数的拷贝构造和赋值运算符重载进行了保护,直接不能拷贝了,其他成员函数和AutoPtr一样。

模拟实现scoped_array

template<class T>
class ScopedArray
{
public:
	ScopedArray(T* ptr)
		:_ptr(ptr)
	{}

	~ScopedArray()
	{
		if (_ptr)
		{
			printf("delete[] 0x%p\n", _ptr);
			delete[] _ptr;
		}
	}

	T& operator[](size_t pos)
	{
		return _ptr[pos];
	}

protected:
	ScopedArray(const ScopedArray<T>& s);
	ScopedArray<T>& operator=(const ScopedArray<T>& s);

protected:
	T* _ptr;
};

ScopedArray与ScopedPtr相比只重载了operate[]

(2)shared_ptr/shared_array  采用引用计数

模拟实现 shared_ptr

template<class T, class D = Deleter>
class SharedPtr
{
public:
	SharedPtr(T* ptr = NULL, D del = Deleter())
		:_ptr(ptr)
		,_refCount(new int(1))
		,_del(del)

	{}

	void Release()
	{
		if (--(*_refCount) == 0)
		{
			if (_ptr)
			{
				printf("delete: 0x%p\n", _ptr);
				//delete _ptr;
				_del(_ptr);
			}

			delete _refCount;
		}
	}

	~SharedPtr()
	{
		Release();
	}

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

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

	// sp2(sp1)
	SharedPtr(const SharedPtr<T, D>& sp)
		:_ptr(sp._ptr)
		,_refCount(sp._refCount)
	{
		++(*_refCount);
	}

	// sp2 = sp3
	SharedPtr<T, D>& operator=(const SharedPtr<T, D>& sp)
	{
		if (_ptr != sp._ptr)
		{
			Release();

			_ptr = sp._ptr;
			_refCount = sp._refCount;
			++(*_refCount);
		}

		return *this;
	}

protected:
	T* _ptr;
	int* _refCount;

	D _del;
};

模拟实现 shared_array

template<class T>
class SharedArray
{
public:
	SharedArray(T* ptr = NULL)
		:_ptr(ptr)
		,_refCount(new int(1))

	{}

	void Release()
	{
		if (--(*_refCount) == 0)
		{
			if (_ptr)
			{
				printf("delete: 0x%p\n", _ptr);
				delete[] _ptr;
			}
		
			delete _refCount;
		}
	}

	int RefCount()
	{
		return *_refCount;
	}

	T& operator[](size_t pos)
	{
		return _ptr[pos]
	}

	~SharedArray()
	{
		Release();
	}

	// sp2(sp1)
	SharedArray(const SharedArray<T>& sp)
		:_ptr(sp._ptr)
		,_refCount(sp._refCount)
	{
		++(*_refCount);
	}

	// sp2 = sp3
	SharedArray<T>& operator=(const SharedArray<T>& sp)
	{
		if (_ptr != sp._ptr)
		{
			Release();

			_ptr = sp._ptr;
			_refCount = sp._refCount;
			++(*_refCount);
		}

		return *this;
	}

protected:
	T* _ptr;
	int* _refCount;
};

//struct AA
//{
//	~AA()
//	{
//		cout<<"~AA()"<<endl;
//	}
//};

struct DelArray
{
	template<class T>
	void operator()(T* ptr)
	{
		delete[] ptr;
	}
};
 *对于shared_array不小心就会引发new/delete与new[]/delete[]的匹配问题,不匹配就可能会出现问题:

【1】.内置类型:不匹配不出现问题,但是最好匹配使用;

【2】.自定义类型:当自定义类型有自己的析构函数时,编译器会在开辟的空间前多开四个字节,这时不匹配使用,释放时就少释放了四个字节,造成内存泄漏;而自己没有定义析构函数时,编译器会优化不多开四个字节,不匹配不会出现问题。

(3)weak_ptr  解决循环引用问题

什么是循环引用?

第一块空间被下一块空间的_prev管理,而下一块空间被前一块空间的_next管理,构成循环,使得空间无法释放,用weak_ptr同shared_ptr构成友元可以解决。
模拟实现

template<class T>
class WeakPtr
{
public:
	WeakPtr()
		:_ptr(NULL)
	{}

	WeakPtr(const SharedPtr<T>& sp)
		:_ptr(sp._ptr)
	{}

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

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

private:
	T* _ptr;
};

struct ListNode
{
	WeakPtr<ListNode> _prev;
	WeakPtr<ListNode> _next;
	/*SharedPtr<ListNode>_next;
        ListNode>_prev;*/
};

void TestCycleRef()
{
	// 循环引用
	SharedPtr<ListNode> cur = new ListNode;
	SharedPtr<ListNode> next = new ListNode;

	cur->_next = next;
	next->_prev = cur;
}




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值