智能指针
一、什么是智能指针?
资源分配即初始化,定义一个类来封装资源的分配和释放,在构造函数完成资源的分配和初始化,在析构函数完成资源的清理,可以保证资源的正确初始化和释放。
对于编译器来说,智能指针实际上是一个栈对象,并非指针类型,在栈对象生命期即将结束时,智能指针通过析构函数释放有它管理的堆内存。所有智能指针都重载了“operator->”操作符,直接返回对象的引用,用以操作对象。访问智能指针原来的方法则使用“.”操作符。二、为什么要有智能指针的存在?
由于C++语言没有内存的自动回收机制,程序员每次new出来的内存都要手动delete释放,而当程序员忘delete,或者流程复杂,异常导致没有运行到delete提前退出,就会造成内存泄漏,防不胜防,则用智能指针可以有效缓解这些问题。
三、智能指针的发展
1.Auto_ptr 的管理权转移(了解机制,不建议使用)
用一段代码配合解析
template<class T>
class AutoPtr
{
public:
AutoPtr(T* ptr)
:_ptr(ptr)
{}
~AutoPtr()
{
if (_ptr)
{
printf("delete:0x%p\n", _ptr);
delete _ptr;
}
}
T& operator*()
{
return *_ptr;
}
T* operator->()
{
return _ptr;
}
// ap2(ap1)
AutoPtr(AutoPtr<T>& ap)
:_ptr(ap._ptr)
{
ap._ptr = NULL;
}
// ap2 = ap3
AutoPtr<T>& operator=(AutoPtr<T>& ap)
{
if (this != &ap)
{
if (_ptr)
{
printf("delete:0x%p\n", _ptr);
delete _ptr;
}
_ptr = ap._ptr;
ap._ptr = NULL;
}
return *this;
}
protected:
T* _ptr;
};
void TestAutoPtr()
{
int* p = new int(10);
AutoPtr<int> ap1(p);
AutoPtr<int> ap2(ap1);
AutoPtr<int> ap3(ap2);
AutoPtr<int> ap4(new int(20));
ap3 = ap4;
}
管理权转移的方法会导致有些对象悬空,解引用它们就会使程序崩溃。例如上面代码中
这块空间的管理权从ap1转给ap2,ap1则指向空,因此不能再使用,同理代码中的p,ap2,ap4都不能再使用。
2.Boost库的智能指针(ps:新的C++11标准中已经引入了unique_ptr/shared_ptr/weak_ptr)
(1)scoped_ptr/scoped_array 防拷贝(只声明不定义,声明成私有)
模拟实现scoped_ptr
template<class T>
class ScopedPtr
{
public:
// RAII
ScopedPtr(T* ptr)
:_ptr(ptr)
{}
~ScopedPtr()
{
printf("delete:0x%p\n", _ptr);
delete _ptr;
}
// 像指针一样--智能指针的特性
T& operator*()
{
return *_ptr;
}
T* operator->()
{
return _ptr
}
// 防拷贝
// 1.只声明不实现
// 2.声明成私有保护
private:
ScopedPtr(const ScopedPtr<T>&);
ScopedPtr<T>& operator=(const ScopedPtr<T>&);
protected:
T* _ptr;
};
void TestScopedPtr()
{
ScopedPtr<int> sp1(new int(10));
ScopedPtr<int> sp2(sp1);
}
ScopedPtr对成员函数的拷贝构造和赋值运算符重载进行了保护,直接不能拷贝了,其他成员函数和AutoPtr一样。
模拟实现scoped_array
template<class T>
class ScopedArray
{
public:
ScopedArray(T* ptr)
:_ptr(ptr)
{}
~ScopedArray()
{
if (_ptr)
{
printf("delete[] 0x%p\n", _ptr);
delete[] _ptr;
}
}
T& operator[](size_t pos)
{
return _ptr[pos];
}
protected:
ScopedArray(const ScopedArray<T>& s);
ScopedArray<T>& operator=(const ScopedArray<T>& s);
protected:
T* _ptr;
};
ScopedArray与ScopedPtr相比只重载了operate[]
(2)shared_ptr/shared_array 采用引用计数
模拟实现 shared_ptr
template<class T, class D = Deleter>
class SharedPtr
{
public:
SharedPtr(T* ptr = NULL, D del = Deleter())
:_ptr(ptr)
,_refCount(new int(1))
,_del(del)
{}
void Release()
{
if (--(*_refCount) == 0)
{
if (_ptr)
{
printf("delete: 0x%p\n", _ptr);
//delete _ptr;
_del(_ptr);
}
delete _refCount;
}
}
~SharedPtr()
{
Release();
}
T& operator*()
{
return *_ptr;
}
T* operator->()
{
return _ptr;
}
// sp2(sp1)
SharedPtr(const SharedPtr<T, D>& sp)
:_ptr(sp._ptr)
,_refCount(sp._refCount)
{
++(*_refCount);
}
// sp2 = sp3
SharedPtr<T, D>& operator=(const SharedPtr<T, D>& sp)
{
if (_ptr != sp._ptr)
{
Release();
_ptr = sp._ptr;
_refCount = sp._refCount;
++(*_refCount);
}
return *this;
}
protected:
T* _ptr;
int* _refCount;
D _del;
};
模拟实现 shared_array
template<class T>
class SharedArray
{
public:
SharedArray(T* ptr = NULL)
:_ptr(ptr)
,_refCount(new int(1))
{}
void Release()
{
if (--(*_refCount) == 0)
{
if (_ptr)
{
printf("delete: 0x%p\n", _ptr);
delete[] _ptr;
}
delete _refCount;
}
}
int RefCount()
{
return *_refCount;
}
T& operator[](size_t pos)
{
return _ptr[pos]
}
~SharedArray()
{
Release();
}
// sp2(sp1)
SharedArray(const SharedArray<T>& sp)
:_ptr(sp._ptr)
,_refCount(sp._refCount)
{
++(*_refCount);
}
// sp2 = sp3
SharedArray<T>& operator=(const SharedArray<T>& sp)
{
if (_ptr != sp._ptr)
{
Release();
_ptr = sp._ptr;
_refCount = sp._refCount;
++(*_refCount);
}
return *this;
}
protected:
T* _ptr;
int* _refCount;
};
//struct AA
//{
// ~AA()
// {
// cout<<"~AA()"<<endl;
// }
//};
struct DelArray
{
template<class T>
void operator()(T* ptr)
{
delete[] ptr;
}
};
*对于shared_array不小心就会引发new/delete与new[]/delete[]的匹配问题,不匹配就可能会出现问题:
【1】.内置类型:不匹配不出现问题,但是最好匹配使用;
【2】.自定义类型:当自定义类型有自己的析构函数时,编译器会在开辟的空间前多开四个字节,这时不匹配使用,释放时就少释放了四个字节,造成内存泄漏;而自己没有定义析构函数时,编译器会优化不多开四个字节,不匹配不会出现问题。
(3)weak_ptr 解决循环引用问题
什么是循环引用?
第一块空间被下一块空间的_prev管理,而下一块空间被前一块空间的_next管理,构成循环,使得空间无法释放,用weak_ptr同shared_ptr构成友元可以解决。
模拟实现
template<class T>
class WeakPtr
{
public:
WeakPtr()
:_ptr(NULL)
{}
WeakPtr(const SharedPtr<T>& sp)
:_ptr(sp._ptr)
{}
T& operator*()
{
return *_ptr;
}
T* operator->()
{
return _ptr;
}
private:
T* _ptr;
};
struct ListNode
{
WeakPtr<ListNode> _prev;
WeakPtr<ListNode> _next;
/*SharedPtr<ListNode>_next;
ListNode>_prev;*/
};
void TestCycleRef()
{
// 循环引用
SharedPtr<ListNode> cur = new ListNode;
SharedPtr<ListNode> next = new ListNode;
cur->_next = next;
next->_prev = cur;
}