在c++中,动态内存的管理是通过一对运算符来完成的:
new
//在动态内存中为对象分配空间并返回一个指向该对象的指针,我们并可以选择对对象初始化
delete
// 接受一个动态对象的指针,销毁该对象,并释放与之关联的内存
动态内存的使用很容易出现问题,因为确保在正确的时间释放内存是相当困难的,有时我们会忘记释放内存,在这种情况下会产生内存泄漏,有时在尚有指针引用内存的情况下我们就释放了它,在这种情况下就会产生引用非法内存的指针。
int* p = new int[10];
FILE *pfile = fopen("smart_pointer.cpp", "r");
if (pfile == NULL)
{
return; //当文件不存在时,就会导致内存泄露
}
if (p)
{
delete[]p;
p = NULL;
}
为了更容易的使用动态内存,新的标准库提出了两种智能指针来管理动态对象,智能指针的行为类似于常规指针,重要的区别是,它负责自动释放所指向的对象,在新的标准库中提出了一种思想,RAII(资源分配与初始化),这是一种规范,一种解决问题的思想,类似于定义一个类,构造函数(资源分配与初始化),析构函数(清理资源与释放空间),其中智能指针就是RAII的一种应用,智能的管理着指针释放的问题
//c++标准库中
#include<memory.h>
shared_ptr //允许多个指针指向同一个对象
unique_ptr //“独占”所指向的对象
weak_ptr //伴随类,弱引用,指向shared_ptr所管理的对象
模拟实现auto_ptr(只可以管理一个对象)
#include <iostream>
#include <string.h>
using namespace std;
template <class T>
class Autoptr
{
public:
Autoptr(T * ptr)
:_ptr (new T(1))
{
_ptr = ptr;
cout << "Autoptr()" << endl;
}
Autoptr(Autoptr& ptr)
:_ptr (ptr._ptr)
{
cout << "Autoptr(Autoptr& ptr)" << endl;
ptr._ptr = NULL;
}
Autoptr<T>& operator= (Autoptr& ptr)
{
cout << "Autoptr<T>& operator= (Autoptr& ptr)" << endl;
if (this != &ptr)
{
delete this->_ptr;
_ptr = ptr._ptr;
ptr._ptr = NULL;
}
return *this;
}
T& operator*() //*重载
{
return *_ptr;
}
T* operator->() //->重载
{
return _ptr;
}
~Autoptr()
{
cout << "~Autoptr()" << endl;
if (_ptr)
{
delete _ptr;
_ptr = NULL;
}
}
private:
T *_ptr;
};
void Funtest1()
{
Autoptr<int> ptr = new int;
*ptr = 10;
Autoptr<int> ptr1(ptr);
cout << *ptr1<< endl;
}
int main()
{
Funtest1();
return 0;
}
auto_ptr弊端:只能对单个对象进行管理,每次只有一个对象可使用申请的空间,当资源转移给另一个对象使用时,前一个对象就会赋值为空。
当了解了auto_ptr的功能,我们可以用另一种思路来实现它,定义一个私有成员来标记当前对象的状态,当对象正在占用空间时,定义为true,当对象不再指向任何空间时,定义为false. 不过还是切记不要使用auto_ptr;
模拟实现scoped_ptr
与 auto_ptr 一样,都是管理单个对象的, 它的作用是:在一个类中防止拷贝,或许我们可以认为,把它定义为私有的,其实并不然,因为访问私有成员或函数的方式还是会调用函数声明,在这里,我们学习一种新的防拷贝方式:只声明不定义,且将拷贝构造与赋值运算符重载声明为私有即可
代码如下:
实现scoped_ptr
template <class T>
class Scopedptr
{
public:
Scopedptr( T* ptr)
:_ptr(new T)
{
_ptr = ptr;
}
~Scopedptr()
{
if (_ptr)
{
delete _ptr;
_ptr = NULL;
}
}
T& operator*()
{
return *_ptr;
}
T* operator->()
{
return _ptr;
}
private:
Scopedptr(const Scopedptr&);
Scopedptr<T>& operator=(const Scopedptr&);
T* _ptr;
};
void Funtest1()
{
Scopedptr<int> p = new int;
*p = 10;
cout << *p << endl;
/*Scopedptr<int>p1(p);
Scopedptr<int> p2 = p;*/
}
int main()
{
Funtest1();
return 0;
}
scoped_ptr 的实现机制体现了它的独占性,当一个对象占用空间时, 其他对象将无法使用,并且实现不了资源的转移。
注:在STL源码库中,使用的是scoped_ptr 而在boost库中,使用的是unique_ptr,其实两个完全一样
shared_ptr:可以管理多个对象他们之间的资源是共享的(用引用计数的方法实现)
template <class T>
class Sharedptr
{
public:
Sharedptr(T* ptr =NULL)
:_ptr(ptr)
,_pcount(new int(1))
{}
Sharedptr(Sharedptr& p)
:_ptr(p._ptr)
, _pcount(p._pcount)
{
++*(_pcount);
}
Sharedptr<T>& operator=(Sharedptr& p)
{
if (_ptr != p._ptr)
{
if (_ptr == NULL)
{
_ptr = p._ptr;
_pcount = p._pcount;
}
else if (_ptr && (*_pcount == 1))
{
delete _ptr;
_ptr = p._ptr;
_pcount = p._pcount;
}
else
{
--(*_pcount);
_ptr = p._ptr;
_pcount = p._pcount;
}
if (p._ptr)
{
++(*_pcount);
}
}
return *this;
}
~Sharedptr()
{
if (_ptr && --(*_pcount) == 0)
{
delete _ptr;
_ptr = NULL;
}
}
T& operator*()
{
return *_ptr;
}
T* operator->()
{
return _ptr;
}
int Get_pcount()
{
if (_pcount != NULL)
return *_pcount;
return 0;
}
private:
T* _ptr;
int * _pcount;
};
int main()
{
Sharedptr<int> ps = new int(1);
Sharedptr<int> ps1(ps);//ps1是由ps拷贝构造出来的,此操作会递增ps中的引用计数,
Sharedptr<int> ps2;
ps2 = ps1;//ps1与ps2都是Sharedptr,所保存的指针必须可以相互转化,此操作会递减ps2的引用计数,递增q的引用计数,若ps2的引用计数为0,则将其管理的原内存释放
cout << ps.Get_pcount() << endl;
cout << ps1.Get_pcount() << endl;
cout << ps2.Get_pcount() << endl;
return 0;
}
我们可以认为,每个shareptr都有一个关联的计数器,无论何时我们拷贝一个shareptr,计数器都会递增,例如,当用一个对象去初始化另一个对象时,或者将其作为参数传递给一个函数,以及作为函数的返回值,他所关联的计数器都会递增,当我们给shareprt赋予一个新值,或者shareptr被销毁时,计数器就会递减,一旦有一个计数器变为了0,就会自动释放(通过析构函数来完成收尾工作)所管理的内存。
weak_ptr
是一种不控制所指向对象生存周期的智能指针,他指向由一个shared_ptr所管理的对象,将一个 weak_ptr绑定到一个shared_ptr不会改变shareptr的引用计数,一旦最后一个指向对象的shareptr被销毁时,对象就会被释放,即使有weak_ptr指向对象,对象也还是会被释放,因此weak_ptr的名字抓住了这种智能指针“”弱“”共享的特点
当我们创建一个weak_ptr时,会用shareptr来初始化
Sharedptr<int> ps = new int(1);
weak_ptr<int> ps1(ps);//ps1弱共享ps,ps的引用计数未发生改变,ps1所指向的对象可能会被释放掉