一个程序中可能会因为各种情况导致内存泄漏的问题,程序泄漏可能不会一下子被写程序的人发现,因为它可能是一点点的被泄漏,直到内存被耗尽之后才会知道内存泄漏这个问题,但是我们不应该写出这样的程序来坑我们自己。因此在写程序的时候应该尽量避免这类问题的产生
执行流跳转是什么??在一个程序顺序往下执行的时候,在遇到return , break, goto,continue等语句的时候,就会引发执行流跳转。假如说我们在程序开头部分new了一个变量或其他,那么与它配套使用的delete就应该出现在它的下一句,但是可能在他们两中间要有一些条件执行,这些条件中的某些语句可能引发程序结束或退出,那么后面的delete语句则不会被执行,也就是说我们前面申请的空间没有还给系统,这样就会导致内存的泄漏。
如何解决这个问题?RAII(资源分配即初始化)就可以解决。RAII是一种解决问题的思想,用一个类来封装资源的分配和释放,不是具体的某种实现的类或者函数,基于它的思想提出的智能指针是解决问题的根本。
智能指针在快速发展的互联网发展过程中也分为如下几类:
AutoPtr:自动指针管理权转移
template <class T>
class AutoPtr
{
public:
AutoPtr(T* ptr) :_ptr(ptr)
{}
~AutoPtr()
{
if (_ptr)
{
cout << "haha" << endl;
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;
_ptr = ap._ptr;
ap._ptr = NULL;
}
return *this;
}
//运算符*的重载
T& operator*()
{
return *_ptr;
}
//运算符->的重载
T* operator->()
{
return _ptr;
}
private:
T* _ptr;
};
struct auto
{
int a1;
int a2;
};
void Func()
{
int *p1 = new int(2);
*p1 = 10;
AutoPtr<int> ap1(new int(2));
*ap1 = 10;//运算符*的重载
AutoPtr<auto> ap2(new auto);
ap2->a1 = 10;
ap2->a2 = 20;//->的重载 当指向结构体成员的时候会用到->
}
void testAutoPtr()
{
Func();
}
通过程序运行可以看到结果是输出了两次haha,可以看到func函数中没有写任何delete,但是它却释放了开辟的空间。这就是AutoPtr的作用。
ScopedPtr:防拷贝 使用简单 直接将拷贝构造函数设为私有的,这样就不会让一块空间被多个指针指向,并被多次释放。
template<class T>
class ScopedPtr
{
public:
ScopedPtr(T* ptr) :_ptr(ptr)
{}
~ScopedPtr()
{
if (_ptr)
delete[] _ptr;
}
//*的重载
T& operator*()
{
return *_ptr;
}
T* operator->()
{
return _ptr;
}
//为什么设为私有的??就是为了防拷贝,所以只声明不实现
private:
//拷贝构造函数
ScopedPtr(ScopedPtr<T>& sp);
//赋值运算符的重载
ScopedPtr<T>& operator=(ScopedPtr<T>& sp);
private:
T* _ptr;
};
void testScopPtr()
{
ScopedPtr<int> sp(new int(2));
//当想要拷贝的时候会让程序崩掉,这样就不会出现一个空间多次释放的情况
ScopedPtr<int> sp1(sp);
}
SharedPtr:引用计数指针 由于会出现引用循环的问题,所以 配合weakptr使用
循环引用:在引用计数问题上,会出现循环引问题,为什么?假如结构体设为一个链表,那么在释放_prev,_next的时候就会出现问题,什么问题??_prev指向cur,_next指向next,出了作用域之后会cur和next都不指向其空间了,但是空间还有指向它的指针,所以就不会被释放,被释放的空间互相依赖,所以陷入了一个循环
看图可能会更加明白
template <class T>
class WeakedPtr;
template<class T>
class SharePtr
{
public:
friend class WeakedPtr<T>;
SharePtr(T* ptr = NULL) :_ptr(ptr), _refCount(new int(1))
{}
~SharePtr()
{
if (--(*_refCount) == 0)
{
delete _ptr;
delete _refCount;
}
}
//s1(s2)
SharePtr(const SharePtr<T>& sp) :_ptr(sp._ptr), _refCount(sp._refCount)
{
(*_refCount)++;
}
//sp1 = sp2
SharePtr<T>& operator=(SharePtr<T>& sp)
{
if (_ptr != sp._ptr)
{
if (--(*_refCount) == 0)
{
delete _ptr;
delete _refCount;
}
_ptr = sp._ptr;
_refCount = sp._refCount;
(*_refCount)++;
}
return *this;
}
//为了像指针一样才进行*\->的重载
//->的重载
T* operator->()
{
return _ptr;
}
//*的重载
T& operator*()
{
return *_ptr;
}
//查看引用计数的多少
int UseCount()
{
return *_refCount;
}
private:
T* _ptr;
int* _refCount;//一块空间有一个指针
};
//解决了循环引用问题
template <class T>
class WeakedPtr
{
public:
WeakedPtr() :_ptr(NULL)
{}
WeakedPtr(const SharePtr<T>& sp)
:_ptr(sp._ptr)
{}
WeakedPtr<T>& operator=(const SharePtr<T>&sp)
{
_ptr = sp._ptr;
return *this;
}
T& operator* ()
{
return *_ptr;
}
T* operator->()
{
return _ptr;
}
private:
T* _ptr;
};
struct ListNode
{
int _data;
WeakedPtr<ListNode> _next;
WeakedPtr<ListNode> _prev;
~ListNode()
{
cout << "~ListNode" << endl;
}
};
void testSharePtr()
{
//SharePtr<int> sp1(new int(1));
//SharePtr<int> sp2(sp1);
SharePtr<ListNode> cur(new ListNode);
SharePtr<ListNode> next(new ListNode);
cout << cur.UseCount() << endl;
cout << next.UseCount() << endl;
}
来源:CSDN
作者:叫我豆黄
链接:https://blog.csdn.net/qq_36474990/article/details/78748895