智能指针的源由:
c/c++之所以强大,一部分功劳应归结于c/c++语言对指针的支持,有了指针c/c++便可高效、方便的操纵内存,但大部分事物总是不可避免的具有两面性,指针也不例外。指针可以高效、方便的操纵内存极大的提高了c/c++程序员编程的灵活性,但同时指针也给初级c/c++程序员带来了一些大麻烦,为什么这么说呢?请看下面一个例子:
//智能指针源由
#include<iostream>
using namespace std;
int main()
{
int* p = new int(0);
//.....
//.....做好多事情
//内存泄漏
//忘记delete p
return 0;
}
不管是初级程序员还是高级程序员在编写程序时都不可避免的会遇到诸如此类的”内存泄露问题“,内存泄露会慢慢的吃掉计算机的内存空间,最后导致程序崩溃或“操作系统崩溃”。毫无疑问这样的问题将会给程序员带来天大的麻烦,但是随着语言的发展,c++语言可以采用RAII
的思想来解决此类问题,由此引入了“智能指针”。
RAII:
初始化分配的资源,定义一个类来初始化分配的资源和释放资源,在构造函数中初始化或分配资源,在析构函数中释放资源,从而保证自动化资源的分配与释放减少程序出错的概率。
智能指针的发展:
智能指针的发展经过了一个漫长而崎岖的道路。
首先:在c++98中引入了自动指针auto_ptr
它的设计思想是“管理权转移”,虽说可以达到自动释放资源的目的,但是它的设计也一直被程序员所诟病,它的的实现可分为两种:①采用标记位bool _owner
来标记具有管理权的指针,当调用拷贝构造或者赋值运算符重载函数时实现管理权的转移,这种实现可能造成野指针问题。②没有标记位,当调用拷贝构造或者赋值运算符重载时使调用者的指针指向管理的空间而自己的指针则置空,这种实现虽不会产生野指针,但是同一块资源只能由一个智能指针来管理。所以推荐不要使用自动指针。
其次:在c++98之后的很长一段时间里c++标准委员会并没有对标准库的智能指针有较大的改动,但auto_ptr
实在是并不好用,于是第三方boost社区便推出了boost库以满足程序员使用智能指针的需求。boost中引入了守卫指针scoped_ptr
它的设计思想简单粗暴,不允许使用拷贝构造和赋值运算符重载函数,将拷贝构造和赋值运算符重载
函数只声明不定义,并且声明为私有成员(防止他人在类外实现)。这种实现,一块资源也只允许一个智能指针进行管理并且不允许转移管理权限。
还引入了共享指针shared_ptr
它的设计思想是采用引用计数实现资源的正确释放,它是一个功能齐全的智能指针,但是在特殊场景会引入“循环引用”的问题(模拟实现时会说到),为了解决循环引用问题又引入了弱引用计数的智能指针weak_ptr
来配合shared_ptr
实现智能指针的使用。weak_ptr
采用只管管理不管释放的思想实现。
最后:由于boost库很优秀,所以在11年c++标准委员会制定的c++11标准时,智能指针很多是参考boost库制定的,标准库中引入了防拷贝指针unique_pre
(类似于boost中的scoped_ptr
)、shared_ptr
、weak_ptr
(后两者设计思想与boost中相同)。至此,智能指针的发展历史介绍完毕。
模拟实现智能指针:
auto_ptr:
法一:
//自动指针auto_ptr--管理权转移
//法一(标记位)
template <typename T>
class AutoPtr
{
public:
AutoPtr(T* ptr)
:_ptr(ptr)
, _owner(true)
{}
AutoPtr(AutoPtr<T>& ap)
:_ptr(ap._ptr)
, _owner(true)
{
ap._owner = false;
}
~AutoPtr()
{
if (_owner == true)
{
cout <<"~AutoPtr()--->"<< *_ptr << endl;
delete _ptr;
_owner = false;
}
}
AutoPtr<T>& operator=(AutoPtr& ap)
{
if (this != &ap && ap._owner)
{
if (_owner == true)
{
delete _ptr;
}
_ptr = ap._ptr;
ap._owner = false;
_owner = true;
}
return *this;
}
T& operator*()
{
return *_ptr;
}
T* operator->()
{
return _ptr;
}
//为了数据隐藏应为protected:但为了验证方便改为public:
public:
T* _ptr;
bool _owner;
};
void TestAutoPtr()
{
AutoPtr<int> ap1 = new int(1);
AutoPtr<int> ap2 = new int(2);
//拷贝构造--管理权转移
AutoPtr<int> ap3(ap2);
if (ap2._owner == false)
{
cout << "权限交接成功!" << endl;
}
ap2 = ap1;
if (ap1._owner == false)
{
cout << "管理权转移成功!" << endl;
}
//对象出作用域自动释放
}
法二:
//法二
template <class T>
class AutoPtr
{
public:
AutoPtr(T* ptr)
:_ptr(ptr)
{}
AutoPtr(AutoPtr& ap)
:_ptr(ap._ptr)
{
ap._ptr = NULL;
}
~AutoPtr()
{
if (_ptr)
{
cout << "~AutoPtr()" << *_ptr << endl;
delete _ptr;
_ptr = NULL;
}
}
AutoPtr& operator=(AutoPtr& ap)
{
if (this != &ap && ap._ptr)
{
if (_ptr)
{
delete _ptr;
}
_ptr = ap._ptr;
ap._ptr = NULL;
}
return *this;
}
T& operator*()
{
return *_ptr;
}
T* operator->()
{
return _ptr;
}
//为了数据隐藏应为protected:但为了验证方便改为public:
public:
T* _ptr;
};
void TestAutoPtr2()
{
AutoPtr<int> ap1 = new int(1);
AutoPtr<int> ap2 = new int(2);
//拷贝构造--管理权转移
AutoPtr<int> ap3(ap2);
if (ap2._ptr == NULL)
{
cout << "权限交接成功!" << endl;
}
ap2 = ap1;
if (ap1._ptr == NULL)
{
cout << "管理权转移成功!" << endl;
}
//对象出作用域自动释放
}
scoped_ptr:
//守卫指针scoped_ptr(防拷贝)
template <class T>
class ScopedPtr
{
public:
ScopedPtr(T* ptr)
:_ptr(ptr)
{}
~ScopedPtr()
{
cout << "~ScopedPtr()--->" << *_ptr << endl;
delete _ptr;
_ptr = NULL;
}
T& operator*()
{
return *_ptr;
}
T* operator->()
{
return _ptr;
}
private:
//拷贝构造、赋值运算符函数1、只声明不定义 2、声明为私有的,防止别人定义。
ScopedPtr(const ScopedPtr& sp);
ScopedPtr& operator=(const ScopedPtr& sp);
protected:
T* _ptr;
};
void TestScopedPtr()
{
ScopedPtr<int> sp1 = new int(1);
ScopedPtr<int> sp2 = new int(2);
//报错,禁止拷贝构造
//ScopedPtr<int> sp3(s1);
}
定制删除器(仿函数):
定制删除器是利用仿函数来实现的,所谓仿函数就是:在类的内部重载()
运算符,利用类的对象调用重载的()
运算符,根据适当的释放方式来释放传入的指针参数指向的内存。
//仿函数---定制删除器
template <class T>
struct __Del
{
void operator()(T* ptr)
{
cout << "删除成功!" << endl;
//依据分配资源方式不同,释放时还可采用delete[] ptr
delete ptr;
ptr = NULL;
}
};
//删除器使用方法
void Test__Del()
{
//删除器的主要使用方法在于将自己制定的删除器类作为模板参数,
//使用时传入匿名对象在类中通过对象调用仿函数实现自定义删除。
int* p = new int(1);
__Del<int> del;
del(p);
}
shared_ptr:
//共享指针SharedPtr
template <class T>//删除器
struct __Del
{
void operator()(T* ptr)
{
delete ptr;
ptr = NULL;
}
};
template <class T,class Del = __Del<T>>
class SharedPtr
{
public:
SharedPtr(T* ptr = NULL,Del del = Del())
:_ptr(ptr)
, _recCount(new int(1))
, _del(del)
{
//验证循环引用问题时要屏蔽掉
cout << "SharedPtr()" << endl;
}
SharedPtr(const SharedPtr<T,Del>& sp)
:_ptr(sp._ptr)
, _recCount(sp._recCount)
, _del(sp._del)
{
(*_recCount)++;
}
~SharedPtr()
{
if (--(*_recCount) == 0)
{
//验证循环引用问题时要屏蔽掉
cout << "~SharedPtr()--->" << *_ptr << endl;
_del(_ptr);
_ptr = NULL;
delete _recCount;
_recCount = NULL;
}
}
SharedPtr<T,Del>& operator=(const SharedPtr<T,Del>& sp)
{
if (--(*_recCount) == 0)
{
_del(_ptr);
delete _recCount;
}
_ptr = sp._ptr;
_recCount = sp._recCount;
(*_recCount)++;
return *this;
}
T& operator*()
{
return *_ptr;
}
T* operator->()
{
return _ptr;
}
T* GetPtr() const
{
return _ptr;
}
//为了数据隐藏应为protected:但为了验证方便改为public:
public:
T* _ptr;
int* _recCount;
Del _del;
};
void TestSharedPtr()
{
SharedPtr<int> sp1 = new int(1);
SharedPtr<int> sp2 = new int(2);
SharedPtr<int> sp3(sp2);
if (*sp2._recCount == 2)
{
cout << "引用计数++成功!" << endl;
}
//释放对象,调用析构函数;
}
循环引用:
循环引用是由如下场景引起的:当ListNode
节点中含有两个共享指针_next
(指向下一个节点)、_prev
(指向前一个节点)时,两个节点构成双向链表的相邻两个节点时,想要释放一个节点时要依赖于另外一个节点中的指向该节点的共享指针构成循环依赖关系,从而造成节点不能释放的问题。
//共享指针的循环循环引用
template <class T>
struct ListNode
{
ListNode(T x)
:_next(NULL)
, _prev(NULL)
, _data(x)
{}
~ListNode()
{
cout << "~ListNode()" << endl;
}
T _data;
SharedPtr<ListNode<T>> _next;
SharedPtr<ListNode<T>> _prev;
};
void LoopRec()
{
typedef ListNode<int> Node;
typedef SharedPtr<Node> SharedPtr;
SharedPtr sp1 = new Node(1);
SharedPtr sp2 = new Node(2);
sp1->_next = sp2;
sp2->_prev = sp1;
}
weak_ptr:
weak_ptr
是用来配合shared_ptr
使用的,采用只管理对象,不负责释放对象(即不增加shared_ptr
的引用计数)的思想配合shared_ptr
来解决循环引用问题。
//weak_ptr弱引用计数的智能指针
template <class T>
class WeakPtr
{
public:
WeakPtr(T* ptr = NULL)
:_ptr(ptr)
{}
WeakPtr(const SharedPtr<T>& sp)
:_ptr(sp.GetPtr())
{}
WeakPtr<T>& operator=(const SharedPtr<T>& sp)
{
_ptr = sp.GetPtr();
return *this;
}
T& operator*()
{
return _ptr->_data;
}
T* operator->()
{
return _ptr;
}
protected:
T* _ptr;
};
利用weak_ptr解决循环引用:
//Weakptr解决循环引用
template <class T>
struct ListNode
{
ListNode(T x)
:_next(NULL)
, _prev(NULL)
, _data(x)
{}
~ListNode()
{
cout << "~ListNode()" << endl;
}
T _data;
WeakPtr<ListNode<T>> _next;
WeakPtr<ListNode<T>> _prev;
};
void LoopRecSolve()
{
typedef ListNode<int> Node;
typedef SharedPtr<Node> SharedPtr;
SharedPtr sp1 = new Node(1);
SharedPtr sp2 = new Node(2);
sp1->_next = sp2;
sp2->_prev = sp1;
}