c++智能指针**
智能指针是什么?智能指针运用了一种思想->RAII(资源分配即初始化)
资源分配及初始化:定义一个类来封装资源的分配和释放,在构造函数初始化对象,析构函数释放对象,可以正确的初始化和释放。
为什么出现智能指针,智能指针能够有效地解决异常安全问题,或者资源未被释放,而导致内存泄漏。例如:中断语句导致代码跳出,导致内存未被释放。
常见的智能指针有哪些?Auto_ptr,Scoped_ptr,Shared_ptr,Weak_ptr;
c++发展在c++98标准时出现了Auto_ptr(不完善的智能指针):即两个指针无法共同管理一块相同的空间(管理权的转移);接着在c++发展的过程中出现了boost库,boost库中完善了Auto_ptr所不完全的功能出现了Scoped_ptr(防拷贝指针);Shared_ptr(引用计数);Weak_ptr(不计数,与shared_ptr结合使用);
Auto_ptr:
template<class T>
class Autoptr
{
public:
Autoptr(T* ptr = NULL)
:_ptr(ptr)
{}
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;
}
~Autoptr()//析构函数
{
cout<<"~Autoptr()"<<endl;
delete _ptr;
}
private:
T* _ptr;
};
由于Auto_ptr无法实现多个指针共同管理一块内存空间,因此被称为失败的作品;在boost库中产生了Scoped_ptr,C++11中称为unique_ptr原理差不多是相同的,在保护中只声明赋值操作符的重载函数,和拷贝构造函数;
Scoped_ptr:
template<class T>
class Scopedptr
{
public:
Scopedptr(T* ptr = NULL)
:_ptr(ptr)
{}
T& operator*()
{
return *_ptr;
}
T* operator->()
{
return _ptr;
}
~Scopedptr()
{
cout<<"~Scopedptr()"<<endl;
delete _ptr;
}
protected:
Scopedptr(Scopedptr<T>& ap);
Scopedptr<T>& operator=(Scopedptr<T>& ap);
private:
T* _ptr;
};
然而这两种指针没有解决多个指针共同管理一块内存空间,接着Shared_ptr采用引用计数的方法解决了这个问题,在Shared_ptr中定义一个整型的指针,多开辟一块空间,存放引用计数,解决在析构时,析构多次的问题。
Shared_ptr:
template <class T>
class Sharedptr
{
public:
Sharedptr(T* ptr = NULL)
:_ptr(ptr)
,_c(new int(1))
{}
Sharedptr(Sharedptr<T>& ap)
{
_ptr = ap._ptr;
_c = ap._c;
(*_c)++;
}
Sharedptr<T>& operator=(const Sharedptr<T>& ap)
{
if(_ptr != ap._ptr)
{
if((*_c)!=1)
{
*_c--;
_ptr = ap._ptr;
_c = ap._c;
(*_c)++;
}
else
{
delete _ptr;
_ptr = ap._ptr;
_c = ap._c;
(*_c)++;
}
}
return *this;
}
T& operator*()
{
return *_ptr;
}
T* operator->()
{
return _ptr;
}
~Sharedptr()
{
if((*_c)!=1)
{
_ptr = NULL;
(*_c)--;
}
else
{
cout<<"~Sharedptr()"<<endl;
delete _ptr;
delete _c;
}
}
private:
T* _ptr;
int* _c;
};
Shared_ptr很好的解决了多个指针管理一块内存空间,然而出现了新的问题,循环引用的问题,这就要结合Weak_ptr来使用,举个例子:
struct ListNode
{
Sharedptr<ListNode> _prev;
Sharedptr<ListNode> _next;
ListNode()
:_prev(NULL)
,_next(NULL)
{
}
};
//假设用Shared_ptr来管理链表
int main()
{
Sharedptr<ListNode> p1(new ListNode());
Sharedptr<ListNode> p2(new ListNode());
p1->_next = p2;
p2->_prev = p1;
}
/* 这就让p1->next指向p2,p2的引用计数p2._c就会++,
同理p1的引用计数p1._c就会++;这样会导致在析构时,无法析构,
从而导致内存泄漏。*/
解决办法是什么呢?Weak_ptr很好的解决了这个办法:
struct ListNode
{
Weak_ptr<ListNode> _prev;
Weak_ptr<ListNode> _next;
ListNode()
:_prev(NULL)
,_next(NULL)
{
}
};
template<class T>
class Weak_ptr
{
public:
Weak_ptr(T* ptr)
:_ptr(ptr)
{}
~Weak_ptr
{
delete _ptr;
}
private:
T* _ptr
}
解决了循环引用问题,还有个问题Shared_ptr中采用的释放方式时delete
若想开辟一块数组则采用delete[],当然内置类型不用delete[],也不会出错,但是若是开辟string类型的数组则会出现问题,这就要考虑到string的对象模型了,string会在开头多开辟四个字节用来存放个数,然而指针释放则不会向前便宜四个字节,因此又采用了仿函数,用来解决不是内置类型的释放。
仿函数:即在类中重载operator()。
template<class T>
struct less
{
bool operator()(const T& a,const T& b)
{
return a < b;
}
};
int main()
{
cout<<less<int>()(12,22)<<endl;
return 0;
}
上面是仿函数的一个例子,仿函数就是一个类中重载了operator(),可以像普通函数一样使用。
智能指针总结
- 智能指针采用了RAII的思想方法:在构造函数初始化对象,析构函数释放资源。
- 智能指针重载了operator*和operator->,能够像普通指针一样使用。
- 四个智能指针:Auto_ptr;Scoped_ptr;Shared_ptr;每个指针有各自的特点。注意:Shared_ptr,采用了Weak_ptr解决循环引用的问题,定制仿函数解决类型析构问题。