智能指针
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了智能指针相关的知识,希望对你有一定的参考价值。
当我们在编写一些结构复杂的程序的时候,有时候可能需要不断地开辟内存,但是往往就会忘记去释放它,从而造成内存泄露。
使用new和delete来管理动态内存常出的一些错误:
1.忘记delete,即导致了“内存泄漏”,
2.野指针。在对象已经被释放掉之后,(这里注意,此时的指针成为了悬垂指针,即指向曾经存在的对象,但该对象已经不再存在。结果未定义,而且难以检测。)这时候我们再次使用,会产生使用非法内存的指针
于是乎,就出现了智能指针,RALL资源分配及初始化,其实我觉的,智能指针不仅完成了了这些,它还完成了对象的析构。下面就介绍第一种智能指针,也是最最不提倡的一种Autoptr,它是通过管理权转移的形式管理对象内存的。
(1)创建一个指针P1指向一块内存
(2)创建一个指针p2,将P2指向p1的内存,然后p1置空
这种智能指针之所以不提倡,是因为它的缺陷
1、auto_ptr不能共享所有权。
2、auto_ptr不能指向数组3、auto_ptr不能作为容器的成员(不太明白)。
4、不要把auto_ptr放入容器
//1
class Autoptr
{
public:
//构造函数
Autoptr(T* ptr):_ptr(ptr)
{}
//析构函数
~Autoptr()
{
if(_ptr)
{
cout<<"delete"<<_ptr<<endl;
delete _ptr;
_ptr=NULL;
}
}
//拷贝构造函数
Autoptr(Autoptr<T> &ap):_ptr(ap._ptr)
{
ap._ptr=NULL;
}
//赋值运算符符重载
Autoptr<T>& operator=(Autoptr<T> &ap)
{
if(this!=&ap)
{
delete _ptr;
_ptr=ap._ptr;
ap._ptr=NULL;
}
return *this;
}
//->运算符重载
T* operator->()
{
return _ptr;
}
//*运算符重载
T&operator*()
{
return *_ptr;
}
private:
T* _ptr;
};
上边介绍的其实是Autoptr的新版本,其实还有一种旧的版本
//构造函数
Autoptr(T* ptr):_ptr(ptr)
,_owner(ture)
//析构函数
~Autoptr()
{
if(_owner)
{
delete _ptr;
}
}
//拷贝构造函数
Autoptr(Autoptr<T> &ap):_ptr(ap._ptr)
,_owner(true)
{
ap._owner=false;
}
//赋值运算符符重载
Autoptr<T>& operator=(Autoptr<T> &ap)
{
if(this!=&ap)
{
delete _ptr;
_ptr=ap._ptr;
_owner=true;
ap._owner=false;
}
return *this;
}
这种Autoptr实现的时候在数据成员里添加了一个owner来管理对象,看似完美无缺,但是在特殊的场景里就会生成野指针哦,请看下面情景
Autoptr<int>ap1(new int(1));
if(xxx)
Autoptr<int>ap2(ap1);
ap1将管理权给了ap2,此时ap1._owner=false,ap2._owner=true;程序结束的时候会对ap2进行delete,但是此时ap1就成了野指针,当我们解引用ap1的时候会发生错误。程序奔溃!!!
上边说了这么多大概就是Autoptr怎么怎么不好,没事千万不要使用它
于是就诞生了比他稍微能好啦么一点的智能指针scopedptr
scopedptr是一个比较霸气而粗暴的指针,它为了保护好自己的成员,将杜绝一切拷贝和赋值,将拷贝构造函数和赋值运算符只声明不实现,其实在这里声明成delete函数也可以,所以它也不能实现多个指针同时维护同一块空间,这里就不多讲了;
最最重要的还算shardptr了
shardptr采用引用计数的方式,可以使多个指针同时维护一块空间,当指向这个空间的最后一个对象被销毁时,指针才会被删除
template <typename T>
class Shardptr
{
public:
//构造函数
Shardptr(T *ptr)
:_ptr(ptr)
,_pcount(new long(1))
{
}
//析构函数
~Shardptr()
{
if(--(*_pcount)==0)
{
delete _ptr;
delete _pcount;
}
}
//拷贝构造函数
Shardptr(Shardptr<T>& sp)
:_ptr(sp._ptr)
,_pcount(sp._pcount)
{
++(*_pcount);
}
//赋值运算符重载
Shardptr<T>& operator=(const Shardptr<T>& sp)
{
//sp1=sp1
//sp1=sp2
//sp1=sp3
if(this_ptr!=sp._ptr)
{
if(--(*_pcount)==0)
{
delete _ptr;
delete _pcount;
}
_ptr=sp._ptr;
_pcount=sp._count;
++(*_pcount);
}
}
//
T& operator*()
{
return *_ptr;
}
//
T* operator->()
{
return _ptr;
}
//
long Usecount()
{
return *_pcount;
}
private:
T*_ptr;
long*_pcount;
};
weakptr
到这里只能指针算是掌握,应该是了解的差不多了,不过还有一种智能指针没说,那就是weakptr,它是一个弱指针,主要的任务就是帮助shardptr解决循环引用问题,比如当我们在使用shardptr去删除双向链表的时候,会惊奇的发现根本不会调用析构函数。为啥呢,看图你就会明白
这里cur->_next=next;next->_prev=cur;next的prev指向cur,要析构cur ,则需要将next先析构掉,同理想析构next就必须先把cur析构掉,因为这里的cur和Next的引用次数都为2,就这样,造成的后果就是两个节点谁也不能释放;
所以有了weakptr就很好的解决这个问题,它能够保持引用计数不变
#include<iostream>
#include<memory>
using namespace std;
struct Node
{
/*shared_ptr<Node> _next;
shared_ptr<Node> _prev;*/
weak_ptr<Node> _next;
weak_ptr<Node> _prev;
~Node()
{
cout<<"delete"<<this<<endl;
}
};
void TestSharedPtr()
{
shared_ptr<Node> cur(new Node());
shared_ptr<Node> next(new Node());
cout<<"赋值前"<<endl;
cout<<"cur:"<<cur.use_count()<<endl;
cout<<"next:"<<next .use_count()<<endl;
cur->_next=next;
next->_prev=cur;
cout<<"赋值后"<<endl;
cout<<"cur:"<<cur.use_count()<<endl;
cout<<"next:"<<next .use_count()<<endl;
}
有关智能指针的总结:
(1)Autoptr:实现了管理权转移,最好不用
(2)scopedptr:简单粗暴的守卫智能指针(防拷贝)
(3)sharedptr:实现多个指针共享一块内存,通过增减引用计数,使得最后一个使用它的对象释放它
(4)weakptr:弱指针,辅助shardptr解决循环引用问题
本文出自 “1” 博客,请务必保留此出处http://10808695.blog.51cto.com/10798695/1760221
以上是关于智能指针的主要内容,如果未能解决你的问题,请参考以下文章
C++编程经验:智能指针 -- 裸指针管得了的我要管,裸指针管不了的我更要管!