智能指针

1.什么是智能指针?
//在C++中,用户必须自己动态申请内存,也必须自己动态释放内存,若用户自己忘记释放内存,则会造成内存泄露,而智能指针就是定义了一个类来解决这个问题,智能指针就是定义了一个类来封装资源的分配和释放,在构造函数中完成资源的分配和初始化,在析构函数中完成资源的清理,可以保证资源的正确初始化和释放。【定义类来 封装资源的分配和释放,在构造中完成分配,在析构中完成释放
简单来说,智能指针解决的是同一块内存空间被重复指向的空间释放问题。

2.模拟实现AutoPtr
template<class T>
class Autoptr          
{
public:
Autoptr(T* ptr)
      :_ptr(ptr)
{}

Autoptr(Autoptr<T>& ap)
:_ptr(ap._ptr)
{
ap._ptr = NULL;
}

Autoptr& operator = (Autoptr<T>& ap)
{
if(this != &ap)
{
if(_ptr)
delete _ptr;
_ptr = NULL;
_ptr = ap._ptr;
ap._ptr = NULL;
}
return *this;
}

~Autoptr()
{
if(_ptr)
delete _ptr;
_ptr = NULL;
}

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

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

T* Get()
{
return _ptr;
}
private:
T* _ptr;
};

void FunTest1()
{
Autoptr<int>p1(new int(1));
Autoptr<int>p2(p1);
Autoptr<int>p3(new int(3));
p3 = p2;  
}
//C++标准程序中auto_ptr的实现方法与这种实现方法一致,但存在着不少的缺陷:
缺陷1:假设p1指向一段内存空间,经过拷贝构造后,p1失去了这段空间的使用权,而p2则变为这段空间的拥有者,再经过赋值重载后,p2失去这段空间的使用权,p3真正拥有这段空间,若通过p1对这段内存空间进行操作,则程序将会崩溃;
缺陷2:AutoPtr中析构函数采用delete释放空间,因此,这种智能型指针仅对非数组类型适用,且申请空间必须用new 来申请;
缺陷3:C++明确禁止将auto_ptr对象作为STL容器的元素;

3.模拟实现ScopedPtr【以独占方式拥有一块内存空间】
class ScopedPtr
{
public:
ScopedPtr(T* ptr)
:_ptr(ptr)
{}

/*ScopedPtr(const ScopedPtr<T>& ap)
:_ptr(ap._ptr)
{ }

ScopedPtr& operator= (const ScopedPtr<T>& ap)
{
if(this != &ap)
{
_release();
_ptr = ap._ptr;
}
return *this;
}*/
~ScopedPtr()
{
_release();
}
private:
ScopedPtr(const ScopedPtr<T>& ap);
ScopedPtr& operator= (const ScopedPtr<T>& ap);

/*ScopedPtr(const ScopedPtr<T>& ap)
:_ptr(ap._ptr)
{ }

ScopedPtr& operator= (const ScopedPtr<T>& ap)
{
if(this != &ap)
{
_release();
_ptr = ap._ptr;
}
return *this;
}*/

void _release()
{
if(_ptr)
delete _ptr;
_ptr = NULL;
}

T* _ptr;
};

//若独占一块内存空间,则要防止这块内存空间被其他人所拥有,则要防止拷贝构造函数和赋值运算符重载函数的使用,因此,在类中,拷贝构造和赋值运算符重载函数共有三种方式存在:
方式1:声明于public部分,易被定义于类外,容易被调用;
方式2:定义于private部分,易被类中public成员函数和友元函数所调用,友元函数不属于该类;
方式3:声明于private部分, 易被类中public成员函数和友元函数所调用,但无定义,因此,无法调用拷贝构造和赋值运算符重载函数,因此,方式3是达到防拷贝目地的最佳措施。

4.模拟实现ScopedArray
template<class T>      //独占模式
class ScopedArray
{
public:
ScopedArray(T* ptr)
:_ptr(ptr)
{
cout<<"ScopedArray()"<<endl;
}
 
        T& operator[](int index)
        {
                return _ptr[index];
        }

~ScopedArray()
{
_release();
cout<<"~ScopedArray()"<<endl;
}
private:
ScopedArray(const ScopedArray<T>& ap);
ScopedArray& operator= (const ScopedArray<T>& ap);

void _release()
{
if(_ptr)
delete[] _ptr;
_ptr = NULL;
}

T* _ptr;
};

void FunTest7()
{
ScopedArray<int>p1(new int[5]);
}

//有以下几点的说明:
说明1: ScopedArray和Scoped Ptr的区别: ScopedArray采用new[]和delete[]来动态申请和释放内存,是用来处理数组的,没有*,->操作符重载,有operator[]操作,无begin()、end()等类似于容器迭代器等的操作函数,而 ScopedPtr则是采用new 和delete来动态申请和释放内存,是用来处理单个对象,无operator[]操作,但有operator->操作;
说明2:库中为什么没有引入 ScopedArray?ScopedArray的使用速度与原始数组一样快,因此,可以用原始数组来代替,且 ScopedArray功能有限,不能动态增长,也没有迭代支持,不能搭配STL算法,仅有一个纯粹的数组接口,除此之外,STL中的vector容器,不仅可以创建动态数组,且在付出了很小的代价之下比 ScopedArray使用更灵活。

5.模拟实现SharedPtr,并定制删除器
class DealFile         //使用仿函数
{
public:
 void operator()(FILE* fp)
{
fclose(fp);
fp = NULL;
}
};

template<class T>      
class NormalMalloc
{
public:
void operator()(T* t)
{
free(t);
t = NULL;
}
};

template<class T>
class NormalNew
{
public:
void operator()(T* t)
{
delete t;
t = NULL;
}
};

template<class T,class comp>
class SharePtr          
{
public:
SharePtr(T* ptr)
      :_ptr(ptr)
  ,_pCount(new int(1))
{
cout<<"SharedPtr()"<<endl;
}

SharePtr(const SharePtr<T,comp>& ap)
:_ptr(ap._ptr)
,_pCount(ap._pCount)
{
    ++(*_pCount);
             cout<<" SharedPtr1()"<<endl;
}

SharePtr& operator =(const SharePtr<T,comp>& ap)
{
if(this != &ap)
{
if(NULL != _ptr && 0 == --(*_pCount))
{
_release();
}
   _ptr = ap._ptr;
   _pCount = ap._pCount;
   ++(*_pCount);
}
return *this;
}

~SharePtr()
{
if(_ptr)
{
 _release();
}

if(_pCount && 0 == --(*_pCount))
{
delete _pCount;    
        _pCount = NULL;
}

cout<<"~SharedPtr()"<<endl;
}

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

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

T* Get()
{
return _ptr;
}
private:
void _release()
{
   comp();
}

T* _ptr;
int* _pCount;
};

void FunTest8()
{
FILE* fp = fopen("1.txt","rb");
SharePtr<FILE,DealFile>p1(fp);

SharePtr< int,NormalNew<int> >p5(new int(3));
         SharePtr< int,NormalNew<int> >p6(p5);
SharePtr< int,NormalNew<int> >p7(new int(5));
p7 = p6;
}

//此次模拟实现的 SharedPtr可以满足不同类型的空间释放(文件关闭、释放new申请出来的空间、释放malloc申请出来的空间),可以解决多个对象指向同一块内存空间的释放问题;

6.什么是SharedPtr的循环引用?
有以下程序代码:

#include<iostream>
#include<memory>
using namespace std;

struct Node  
{
Node(int x)
:_pre(NULL)
                ,_next(NULL)
                ,value(x)
{
cout<<"Node()"<<endl;
}

        shared_ptr<Node> _pre;
shared_ptr<Node> _next;
int value;

~Node()
{
cout<<"~Node()"<<endl;
}
};

void FunTest6()
{
shared_ptr<Node> p1(new Node(5));
cout<<p1.use_count()<<endl;
shared_ptr<Node> p2(new Node(7));
        cout<<p2.use_count()<<endl;

p1->_next = p2;
cout<<p2.use_count()<<endl;
p2->_pre = p1;
cout<<p1.use_count()<<endl;
}


pfirst为一个Node ,psecond也为一个Node,且满足  pfirst->_next = psecond ,psecond->_pre = pfirst;

如图所示,若要释放psecond所指向的空间,则必须要先释放pfirst所指向的空间(psecond->_pre = pfirst;),若要释放pfirst所指向的空间,必须要先释放psecond所指向的空间(pfirst->_next = psecond),因此,造成了循环引用的问题,无法释放所申请的内存空间,造成内存泄露;


为解决shared_ptr的循环引用问题,引入了一个弱智能指针(weak_ptr)来打破循环引用

struct Node  
{
Node(int x)
                :value(x)
{
cout<<"Node()"<<endl;
}

        weak_ptr<Node> _pre;
weak_ptr<Node> _next;
int value;

~Node()
{
cout<<"~Node()"<<endl;
}
};

void FunTest6()
{
shared_ptr<Node> p1(new Node(5));
cout<<p1.use_count()<<endl;
shared_ptr<Node> p2(new Node(7));
        cout<<p2.use_count()<<endl;

p1->_next = p2;
cout<<p2.use_count()<<endl;
p2->_pre = p1;
cout<<p1.use_count()<<endl;
}
分析:



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值