c++智能指针

目录

1.为什么需要智能指针?

2.内存泄漏

3.智能指针的使用及原理

4.C++11和boost中智能指针的关系

5.RAII扩展学习


1.为什么需要智能指针?

 这里有一个代码,分析一下其中有什么会导致内存泄漏的地方

int Div()
{
	int a, b;
	cin >> a >> b;
	if (b == 0)
		throw invalid_argument("除0错误");
	return a / b;
}

void func()
{
	int* p1 = new int;
	int* p2 = new int;

	cout << Div() << endl;

	delete p1;
	delete p2;

}

int main()
{
	try 
	{
		func();
	}

	catch(exception& e)
	{
		cout << e.what() << endl;
	}

	return 0;
}
// 1、如果p1这里new 抛异常会如何?
// 2、如果p2这里new 抛异常会如何?
// 3、如果div调用这里又会抛异常会如何?
分析以上三个问题发现,p1抛异常时不会导致内存泄漏,p2new异常会导致p1 没有delete,没有delete, div调用抛异常会导致p1 p2 没有delete,会导致内存泄漏。

2.内存泄漏

2.1 什么是内存泄漏,内存泄漏的危害

什么是内存泄漏:内存泄漏指因为疏忽或错误造成程序未能释放已经不再使用的内存的情况。内
存泄漏并不是指内存在物理上的消失,而是应用程序分配某段内存后,因为设计错误,失去了对
该段内存的控制,因而造成了内存的浪费。
内存泄漏的危害:长期运行的程序出现内存泄漏,影响很大,如操作系统、后台服务等等,出现
内存泄漏会导致响应越来越慢,最终卡死。

2.2 内存泄漏分类(了解)

C/C++ 程序中一般我们关心两种方面的内存泄漏:
堆内存泄漏 (Heap leak)
堆内存指的是程序执行中依据须要分配通过 malloc / calloc / realloc / new 等从堆中分配的一
块内存,用完后必须通过调用相应的 free 或者 delete 删掉。假设程序的设计错误导致这部分
内存没有被释放,那么以后这部分空间将无法再被使用,就会产生 Heap Leak
系统资源泄漏
指程序使用系统分配的资源,比方套接字、文件描述符、管道等没有使用对应的函数释放
掉,导致系统资源的浪费,严重可导致系统效能减少,系统执行不稳定。

2.4如何避免内存泄漏

1. 工程前期良好的设计规范,养成良好的编码规范,申请的内存空间记着匹配的去释放。 ps
这个理想状态。但是如果碰上异常时,就算注意释放了,还是可能会出问题。需要下一条智
能指针来管理才有保证。
2. 采用 RAII 思想或者智能指针来管理资源。
3. 有些公司内部规范使用内部实现的私有内存管理库。这套库自带内存泄漏检测的功能选项。

3.智能指针的使用及原理

3.1 RAII

RAII Resource Acquisition Is Initialization )是一种 利用对象生命周期来控制程序资源 (如内
存、文件句柄、网络连接、互斥量等等)的简单技术。
在对象构造时获取资源 ,接着控制对资源的访问使之在对象的生命周期内始终保持有效, 最后在
对象析构的时候释放资源 。借此,我们实际上把管理一份资源的责任托管给了一个对象。这种做
法有两大好处:
不需要显式地释放资源。
采用这种方式,对象所需的资源在其生命期内始终保持有效。
使用Rall的思想设计一个SmartPtr类
template
class SmartPtr {
public:
	SmartPtr(T* ptr = nullptr)
		:_ptr(ptr)
	{}

	~SmartPtr()
	{
		if (_ptr)
			delete _ptr;
	}

private:
	T* ptr;
};

然后接着改造一开始的func代码

void func()
{
	SmartPtr p1(new int);
	SmartPtr p2(new int);

	cout << Div() << endl;
}

此时p1和p2已经交给了samrtPtr的对象来管理,当该函数栈帧结束时会自动调用析构函数释放这两个指针,防止内存泄漏。

3.2 智能指针的原理

上述的 SmartPtr 还不能将其称为智能指针,因为它还不具有指针的行为。指针可以解引用,也可
以通过 -> 去访问所指空间中的内容,因此: AutoPtr 模板类中还得需要将 * -> 重载下,才可让其
像指针一样去使用
修改后的例子及使用
emplate
class SmartPtr {
public:
	SmartPtr(T* ptr = nullptr)
		:_ptr(ptr)
	{
	}

	~SmartPtr()
	{
		if (_ptr)
			delete _ptr;
	}

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

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

private:
	T* ptr;
};

struct StuId
{
	int _id;
	string _name;
	int _num;
};
int main()
{

	SmartPtr p1(new int);
	*p1 = 20;

	SmartPtr p2(new StuId);
	p2->_id = 1;
	p2->_name = "张三";
	p2->_num = 119;
	return 0;
}
总结一下智能指针的原理:
1. RAII 特性
2. 重载 operator* opertaor-> ,具有像指针一样的行为

3.3 std::auto_ptr

C++98 版本的库中就提供了 auto_ptr 的智能指针。下面演示的 auto_ptr 的使用及问题。
auto_ptr 的实现原理:管理权转移的思想,下面简化模拟实现了一份myspce ::auto_ptr 来了解它的原
autoptr.h
namespace myspace
{
	template
	class Autoptr
	{
	public:
		Autoptr(T* ptr = nullptr)
			:_ptr(ptr)
		{}
		Autoptr(Autoptr& p)
			:_ptr(p._ptr)
		{
			// 管理权转移
			p._ptr = nullptr;
		}

		Autoptr& operator=(Autoptr& ap)
		{
			if (*this != &ap)
			{
				if (_ptr)
					delete _ptr;
				// 转移ap中资源到当前对象中
				_ptr = ap._ptr;
				ap._ptr = NULL;
			}
			return *this;
		}

		~Autoptr()
		{
			if (_ptr)
			{
				cout << "delete:" << _ptr << endl;
				delete _ptr;
			}
		}

		// 像指针一样使用
		T& operator*()
		{
			return *_ptr;
		}
		T* operator->()
		{
			return _ptr;
		}
	private:
		T* _ptr;
	};
}
main.cpp
int main()
{
	myspace::Autoptr p1(new int(10));

	myspace::Autoptr p2 = p1;

	// // sp1悬空
	*p2 = 10;
	cout << *p2 << endl;
	cout << *p1 << endl;

	return 0;

}

由于p1悬空还进行访问操作会导致程序崩溃,结论:auto_ptr是一个失败设计,很多公司明确要求不能使用auto_ptr

3.4 std::unique_ptr

C++11 中开始提供更靠谱的 unique_ptr
unique_ptr 的实现原理:简单粗暴的防拷贝,下面简化模拟实现了一份 UniquePtr 来了解它的原
	template
	class UniquePtr
	{
	public:
		UniquePtr(T* ptr=nullptr)
			:_ptr(ptr)
		{}
		~UniquePtr()
		{
			if (_ptr)
			{
				cout << "delete:" << _ptr << endl;
				delete _ptr;
			}
		}

		// 像指针一样使用
		T& operator*()
		{
			return *_ptr;
		}
		T* operator->()
		{
			return _ptr;
		}

		UniquePtr(const UniquePtr& sp) = delete;
		UniquePtr& operator=(const UniquePtr& sp) = delete;
	private:
		T* _ptr;
	};
}

3.5 std::shared_ptr

C++11 中开始提供更靠谱的并且支持拷贝的shared_ptr,shared_ptr 的原理:是通过引用计数的方式来实现多个 shared_ptr 对象之间共享资源 ,即共同管理一个资源。
1. shared_ptr 在其内部, 给每个资源都维护了着一份计数,用来记录该份资源被几个对象共
2. 对象被销毁时 ( 也就是析构函数调用 ) ,就说明自己不使用该资源了,对象的引用计数减
一。
3. 如果引用计数是 0 ,就说明自己是最后一个使用该资源的对象, 必须释放该资源
4. 如果不是 0 ,就说明除了自己还有其他对象在使用该份资源, 不能释放该资源 ,否则其他对
象就成野指针了。
	template
	class SharePtr
	{
	public:
		SharePtr(T* ptr=nullptr)
			:_ptr(ptr)
			,_count(new int(1))
		{}

		SharePtr(const SharePtr& sp)
			:_ptr(sp._ptr)
			, _count(sp._count)
		{
			++(*_count);
		}

		template
		SharePtr(T* ptr, D del)
			:_ptr(ptr)
			, _count(new int(1))
			, _del(del)
		{}

		SharePtr& operator=(const SharePtr& sp)
		{
			if(sp._ptr!=_ptr)
			{
				relese();
				_ptr = sp._ptr;
				_count = sp._count;
				++(*_count);
			}
			return *this;
		}

		void relese()
		{
			--(*_count);
			if (*_count == 0 && _ptr)
			{
				cout << "delete:" << _ptr << endl;
				_del (_ptr);
				delete _count;
				_ptr = nullptr;
				_count = nullptr;
			}

		}

		int use_count() const
		{
			return *_count;
		}

		T* get() const
		{
			return _ptr;
		}

		~SharePtr()
		{
			relese();
		}
	private:
		T* _ptr;
		int* _count;
		function _del = [](T* ptr) {delete ptr; };
	};
}

其中还引入了一个定制删除器,可以从外部传入可调用函数释放资源。

4.C++11boost中智能指针的关系

1. C++ 98 中产生了第一个智能指针 auto_ptr.
2. C++ boost 给出了更实用的 scoped_ptr shared_ptr weak_ptr.
3. C++ TR1 ,引入了 shared_ptr 等。不过注意的是 TR1 并不是标准版。
4. C++ 11 ,引入了 unique_ptr shared_ptr weak_ptr 。需要注意的是 unique_ptr 对应 boost
scoped_ptr 。并且这些智能指针的实现原理是参考 boost 中的实现的。

你可能感兴趣的:(c++,开发语言)