1.为什么出现智能指针?
C++智能指针是行为类似于指针的类对象。它使用设计模式中的代理模式,代理了原始“裸”指针的行为,为指针添加了更多更有用的特性。C++引入异常机制后,智能指针由一种技巧升级为一种非常重要的技术,因为如果没有智能指针,程序员必须保证new对象能在正确的时机delete,四处编写异常捕获代码释放资源,而智能指针则可以在退出作用域时,不管是正常离开或是因异常离开作用域也会自动调用析构函数释放在堆栈上动态分配的对象。将“裸”指针包装成智能指针对象可以实现动态分配的内存对象的自动释放。
2.什么是智能指针?
智能指针就是智能/自动的管理指针所指向的动态资源的释放。
3.智能指针发展历史
4.auto_ptr/scoped_ptr/shared_pr/weak_ptr的设计思想、缺陷? 模拟实现auto_ptr/scoped_ptr/shared_pr/weak_ptr
(1) auto_ptr (自动指针)
*作用: 自动释放内存
缺陷:管理权转移——-指在进行拷贝构造等操作时,新拷贝的智能指针替代了原来指针的管理权限。
#include<iostream>
using namespace std;
template<class T>
class AutoPtr
{
public:
AutoPtr(T*ptr) //构造
:_ptr(ptr)
{}
//ap2(ap2)
AutoPtr(AutoPtr<T>&ap) //拷贝构造
:_ptr(ap._ptr)
{
ap._ptr = NULL; //管理权转移
}
//ap2=ap1
AutoPtr<T>& operator=(AutoPtr<T>&ap) //赋值运算符重载
{
if (this != &ap)
{
if (_ptr)
delete_ptr;
_ptr = ap._ptr; //管理权转移
ap._ptr = NULL;
}
return *this;
}
~AutoPtr()
{
if (_ptr)
{
cout << "~AutoPtr()" << endl;
delete _ptr;
}
}
T& operator*()
{
return *_ptr;
}
T* operator->()
{
return _ptr;
}
protected:
T* _ptr;
};
void TestAutoPtr()
{
AutoPtr<int> ap1(new int(10));
AutoPtr<int> ap2=ap1;
//cout << *ap1 << endl;
cout << *ap2 << endl;
}
int main()
{
TestAutoPtr();
getchar();
return 0;
}
用AutoPtr进行拷贝构造和赋值运算符重载时,先释放不为空的ap1,然后由ap2来管理ap1的空间,此时ap1为空,访问ap1会出错.
访问ap2: 程序正常运行,输出析构函数
再次访问ap1:程序出错
(2) scoped_ptr (守卫指针)
作用:防拷贝
简单粗暴,高效,可在不进行拷贝的场景下使用。
template<class T>
class ScopedPtr
{
public:
ScopedPtr(T* ptr)
:_ptr(ptr)
{}
~ScopedPtr()
{
delete _ptr;
}
T& operator*()
{
return *_ptr;
}
T* operator->()
{
return _ptr;
}
//声明成私有
private:
ScopedPtr(const ScopedPtr<T>&ap);
ScopedPtr<T>& operator=(const ScopedPtr<T>&ap);
protected:
T* _ptr;
};
void TestScopedPtr()
{
ScopedPtr<int>ap1(new int(10));
//ScopedPtr<int> ap2(ap1);
}
int main()
{
//TestAutoPtr();
TestScopedPtr();
getchar();
return 0;
}
在使用AutoPtr时,再次访问拷贝构造前的指针会出错,为了解决这一问题就引入乐scoped_ptr.来防止拷贝,具体解决方法 1)进行拷贝等操作时只声明不定义;2)声明设为私有。这样再次访问就不会出错了。
但是实际操作中要进行拷贝构造和赋值运算符等操作,scoped_ptr功能受限,于是引入了shared-ptr,
(3) shared_ptr (共享指针)——-核心
主要思想:引用计数,
缺陷:存在循环引用的问题,但功能齐全
template<class T,class Del>
class SharedPtr
{
public:
SharedPtr()
:_ptr(NULL)
, _refCount(NULL)
{}
SharedPtr(T* ptr = NULL, Del del)
:_ptr(ptr)
, _refCount(new int(1))
, _del(del)
{}
~SharedPtr()
{
if (--(*_refCount) == 0)
{
cout <<"delete"<< endl;
_del(ptr);
delete _refCount;
}
}
SharedPtr(const SharedPtr<T, Del>&sp)
:_ptr(sp._ptr)
, _refCount(sp._refCount)
{
++(*_refCount);
}
//sp2=sp3
SharedPtr<T>&operator=(const SharedPtr<T>&sp)
{
//if(this!=&s)
if (_ptr != sp._ptr)
{
if (--(*_refCount) == 0)
{
delete _ptr;
delete _refCount;
}
_ptr = sp._ptr;
_refCount = sp._refCount;
++(*_refCount);
}
return *this;
}
//现代写法
SharedPtr<T, Del>&operator=(SharedPtr<T, Del>sp)
{
swap(_ptr, sp._ptr);
swap(_refCount, sp._refCount);
return *this;
}
int RefCount()
{
return *_refCount;
}
T& operator*()
{
return *_ptr;
}
T* operator->()
{
return _ptr;
}
T* GetPtr() const
{
return _ptr;
}
protected:
T* _ptr;
int* _refCount;
Del _del;
};
int main()
{
//TestAutoPtr();
//TestScopedPtr();
TestSharedPtr();
getchar();
return 0;
}
引入功能齐全的shared_ptr,其主要设计思想是引用计数,即多个指针指向同一个空间,也就是共享同一个空间,并且依次释放空间直到最后一个指针释放方可完成空间的释放。
但是引入引用计数就会出现循环引用的问题:
*循环引用
struct ListNode
{
SharedPtr<ListNode> _prev;
SharedPtr<ListNode> _next;
ListNode()
:_prev(NULL)
, _next(NULL)
{}
~ListNode()
{
cout << "~ListNode()" << endl;
}
}
};
void TestCycleRef()
{
SharedPtr<ListNode> cur = new ListNode;
SharedPtr<ListNode> next = new ListNode;
cur->_next = next;
next->_prev = cur;
}
int main()
{
TestCycleRef();
return 0;
}
由上图可见,cur和next分别由两个指针共同管理空间,而两个节点的_next和_prev相互依赖,那么两个空间都得不到释放,所以出现了循环引用。
为了解决循环引用问题引入了弱指针。
(4) weak_ptr (弱指针)
作用:配合shared_ptr使用,解决循环引用问题
使用一个弱引用智能指针(weak_ptr)来打破循环引用(weak_ptr不增加引用计数)
template<class T>重点内容
class WeakPtr
{
public:
WeakPtr()
:_ptr(NULL)
{}
WeakPtr(const SharedPtr<T>& sp)
:_ptr(sp.GetPtr())
{}
T& operator*()
{
return *_ptr;
}
T* operator->()
{
return _ptr;
}
protected:
T* _ptr;
};
// 循环引用
struct ListNode
{
WeakPtr<ListNode> _prev;
WeakPtr<ListNode> _next;
~ListNode()
{
cout<<"~ListNode()"<<endl;
}
};
void TestCycleRef()
{
SharedPtr<ListNode> cur = new ListNode;
SharedPtr<ListNode> next = new ListNode;
cur->_next = next;
next->_prev = cur;
}