c++指针和动态内存管理

C++中动态内存的管理主要是使用new/delete表达式和std::allcator类。为了管理动态内存更加安全,C++11新标准库推出了智能指针。

new/delete

new完成的操作:
(1): 它分配足够存储一个特定类型对象的内存
(2):为它刚才分配的内存中的那个对象设定初始值。(对于内置类型对象,就是默认初始化该它,对应类类型,调用constructor初始化)

delete完成的操作:
(1):销毁给定指针指向的对象
(2):释放该对象的对应内存

常见问题

1.申请的空间没有进行释放,会出现内存泄漏。这种忘记释放的内存如果不退出程序将永远不会归还给系统。解决方法:

  • 在离开作用域前释放掉:
int *p=new int(0);
delete p;
p=nullptr;
//释放该指针后指向空,一可以避免再次访问它出现未定义行为,二可以避免再次delete它出现未定义行为。   

  • 接管申请的空间
int *pa;
{
	int *p=new int(0);
	pa=p;
}
delete pa;

2.使用已经释放内存的对象解决方法:对已经释放的内存对象赋给一个空指针,在使用前判断是否为空指针。

int *p=new int(0);
delete p;
p=nullptr;//也可=NULL/0
//......
if(p)
	{......}

3.同一块内存释放两次,虽然这个问题听起来不太可能发生,但当有两个指针指向同一块内存时,可能就容易发生了。避免方法:delete后直接将内存对象置为空指针。

智能指针的使用及原理

什么是智能指针?
智能指针是一个类,这个类的构造函数中传入一个普通指针,析构函数中释放传入的指针。智能指针的类都是栈上的对象,所以当函数(或程序)结束时会自动被释放,

RALL

RALL是一种利用对象生命周期来控制程序资源(如内存、文件句柄、网络连接、互斥量等)的技术。
在对象构造时获取资源,接着控制对资源的访问使之在对象的生命周期内始终保持有效,最后在对象析构的时候释放资源。借此,我们实际上把管理一份资源的责任托管给了一个对象。这种做法有两大好处:

  • 不需要显式地释放资源。
  • 采用这种方式,对象所需的资源在其生命期内始终保持有效。

智能指针的原理

  1. RAII特性
  2. 重载operator*和opertaor->,从而具有指针一样的行为。

使用RALL思想设计智能指针:

template<class T>
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 Date
{
	int _year;
	int _month;
	int _day;
	int main()
	{
 	SmartPtr<int> sp1(new int);
 	*sp1 = 10
 	cout<<*sp1<<endl;
 
 	SmartPtr<int> sparray(new Date);
 	sparray->_year = 2018;
 	sparray->_month = 1;
 	sparray->_day = 1;
  }
}
std::auto_ptr

auto_ptr的实现原理:管理权转移/转移资源的思想。在c++98中提供,尽量不要使用。

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

	~AutoPtr()
	{
		if (_ptr)
			delete _ptr;
	}
	AutoPtr(AutoPtr<T>& ap)
		: _ptr(ap._ptr)
	{
		ap._ptr = NULL;
	}
	AutoPtr<T>& operator=(AutoPtr<T>& ap)
	{
		// 检测是否为自己给自己赋值
		if (this != &ap)
		{
			// 释放当前对象中资源
			if (_ptr)
				delete _ptr;

			// 转移ap中资源到当前对象中
			_ptr = ap._ptr;
			ap._ptr = NULL;
		}

		return *this;
	}
	T& operator*() { return *_ptr; }
	T* operator->() { return _ptr; }
private:
	T* _ptr;
};

注意

  1. 不要使用auto_ptr保存一个非动态开辟空间的指针,因为在作用域结束的时候,会执行智能指针的析构函数,释放这块空间,但非动态的空间又无法释放;
  2. 不要使用两个auto_ptr指针指向同一个指针,因为会产生额外开销;
  3. 不要使用auto_ptr指向一个指针数组,因为auto_ptr的析构函数所用的是delete而不是delete[];
  4. 不要将auto_ptr储存在容器中,因为赋值和拷贝构造后原指针无法使用。
std::unique_ptr

unique_ptr的实现原理:防拷贝,c++11中实现。

template<class T>
class UniquePtr
{
public:
	UniquePtr(T * ptr = nullptr)
		: _ptr(ptr)
	{}
	~UniquePtr()
	{
		if (_ptr)
			delete _ptr;
	}
	T& operator*() { return *_ptr; }
	T* operator->() { return _ptr; }

private:
	// C++98防拷贝的方式:只声明不实现+声明成私有
	UniquePtr(UniquePtr<T> const &);
	UniquePtr & operator=(UniquePtr<T> const &);
	// C++11防拷贝的方式:delete
	UniquePtr(UniquePtr<T> const &) = delete;
	UniquePtr & operator=(UniquePtr<T> const &) = delete;

private:
	T * _ptr;
};
std::shared_ptr

shared_ptr的原理:是通过引用计数的方式来实现多个shared_ptr对象之间共享资源。
注意

  1. shared_ptr在其内部,给每个资源都维护了着一份计数,用来记录该份资源被几个对象共享。
  2. 在对象被销毁时(也就是析构函数调用),就说明自己不使用该资源了,对象的引用计数减一。
  3. 如果引用计数是0,就说明自己是最后一个使用该资源的对象,必须释放该资源;
  4. 如果不是0,就说明除了自己还有其他对象在使用该份资源,不能释放该资源,否则其他对象就成野指针了。
#include <thread>
#include <mutex>
template <class T>
class SharedPtr
{
public:
	SharedPtr(T* ptr = nullptr)
		: _ptr(ptr)
		, _pRefCount(new int(1))
		, _pMutex(new mutex)
	{}
	~SharedPtr() { Release(); }
	SharedPtr(const SharedPtr<T>& sp)
		: _ptr(sp._ptr)
		, _pRefCount(sp._pRefCount)
		, _pMutex(sp._pMutex)
	{
		AddRefCount();
	}
	// sp1 = sp2
	SharedPtr<T>& operator=(const SharedPtr<T>& sp)
	{
		//if (this != &sp)
		if (_ptr != sp._ptr)
		{
			// 释放管理的旧资源
			Release();
			// 共享管理新对象的资源,并增加引用计数
			_ptr = sp._ptr;
			_pRefCount = sp._pRefCount;
			_pMutex = sp._pMutex;

			AddRefCount();
		}
		return *this;
	}
	T& operator*() { return *_ptr; }
	T* operator->() { return _ptr; }
	int UseCount() { return *_pRefCount; }
	T* Get() { return _ptr; }
	void AddRefCount()
	{
		// 加锁或者使用加1的原子操作
		_pMutex->lock();
		++(*_pRefCount);
		_pMutex->unlock();
	}
private:
	void Release()
	{
		bool deleteflag = false;
		// 引用计数减1,如果减到0,则释放资源
		_pMutex.lock();
		if (--(*_pRefCount) == 0)
		{
			delete _ptr;
			delete _pRefCount;
			deleteflag = true;
		}
		_pMutex.unlock();

		if (deleteflag == true)
			delete _pMutex;
	}
private:
	int* _pRefCount; // 引用计数
	T* _ptr; // 指向管理资源的指针 
	mutex* _pMutex; // 互斥锁
};

关于这个智能指针的一些问题:
循环引用
线程安全
内存泄漏

荐:
shared_ptr的实现

发布了39 篇原创文章 · 获赞 4 · 访问量 1121

猜你喜欢

转载自blog.csdn.net/qq_41403559/article/details/104297904