智能指针:
c11里面的四个智能指针:auto_ptr,unique_ptr,shared_ptr,weak_ptr其中后三个是c11支持的,并且第一个已经被c11弃用。本博客主要讲auto_ptr,后面会持续更新其他智能指针。
c98中auto_ptr所做的事情就是动态分配对象以及当对象不再需要时自动执行清理。
举例:
以下不使用auto_ptr的函数就有可能发生内存泄漏:
void fun()
{
Object* op = new Object(10);
cout << op->Value() << endl;
if (op->Value() == 10)
{
return;//内存泄漏
}
delete op;
}
使用auto_ptr指针很好的避免了内存泄漏:函数结束的时候,auto_ptr调用析构函数,可以释放资源。
void fun()
{
auto_ptr<Object> pObja(new Object(10));
if (pObja->Value() == 10)
{
cout << pObja->Value() << endl;
return;
}
return;
}
auto_ptr 源码:
#include <iostream>
#include<vector>
using namespace std;
namespace wwn
{
template<class T>
class auto_ptr
{
private:
T* _Ptr;
bool _Owns;//使用权
public:
typedef T element_type;
explicit auto_ptr(T* p = nullptr) :_Owns(p!=nullptr),_Ptr(p){}
auto_ptr(const auto_ptr<T> & _Y) :_Owns(_Y._Owns), _Ptr(_Y.release){} //错误:按照设计的先后顺序初始化,所以会先执行_ptr(_Y.release)
auto_ptr& operator=(const auto_ptr<T>& _Y)
{
if (this != &_Y)
{
if (_Ptr != _Y.get())
{
if (_Owns)
delete _Ptr;
_Owns = _Y._Owns;
}
else if(_Y._Owns)
{
_Owns = true;
}
_Ptr = _Y.release();
}
return *this;
}
~auto_ptr()
{
if (_Owns)
{
delete _Ptr;
}
_Ptr = NULL;
}
void reset(T* p = NULL)
{
if (_Owns)
{
delete _Ptr;
}
_Ptr = p;
}
T* get() const
{
return _Ptr;
}
T & operator*() const
{
return (*get());
}
T* operator->()const
{
return get();
}
T* release()const
{
T* tmp = NULL;
if (_Owns)
{
((auto_ptr<T>*)this)-> _Owns = false;
tmp = _Ptr;
((auto_ptr<T>*)this)->_Ptr = NULL;
}
return tmp;
}
};
}
class Object
{
private:
int value;
public:
Object(int x=0):value(x){}
~Object(){}
int& Value() { return value; }
const int& Value()const { return value; }
void Print() const{ cout << value << endl; }
};
void fun1()
{
//vector<auto_ptr<int> >vec; //错误,绝对不可以这样写
vector<int*> vec;
vec.push_back(new int(10));
vec.push_back(new int(20));
}
void fun()
{
wwn::auto_ptr<Object> oap(new Object(10));
cout << oap->Value() << endl;
oap->Value() = 100;
cout << oap->Value() << endl;
wwn::auto_ptr<int> iap(new int(10));
wwn::auto_ptr<int> ibp;
ibp = iap;
}
int main()
{
fun();
return 0;
}
注意:oap.{ get() ; operator() ; operator() ; ~auto_ptr() ; _ptr} 智能指针 . 后面跟的是智能指针自己的方法以及成员。
oap->{ Value() ; value ; ~Object() } 智能指针-> 后面跟的是智能指针所指对象的属性和方法。
opa->Print();
(*opa).Print();//两个是等价的
auto_ptr的使用以及弃用原因
- 构造函数与析构函数
auto_ptr在构造时获取对某个对象的所有权,在析构时时释放该函数
我们可以这样使用auto_ptr来提高代码的安全性
int* p=new int(0);
auto_ptr<int> ap(p);
这样就不必关心何时是释放p,也不用担心发生异常会有内存泄露。
弃用原因
1)因为auto_ptr析构的时候会删除他所拥有的对象,所以不同的auto_ptr不能指向同一个对象。如下:
int* p=new int(0);
auto_ptr<int> ap1(p);
auto_ptr<int> ap2(p);
这样在ap1和ap2在析构时都会去删除p,两次删除同一个对象的行为在c++标准中是未定义的。
2)
String *str=new String[10];
auto_ptr<String> ap(str);
考虑以上情况,auto_ptr在析构函数中删除针织用的是delete,而不是delete[],所以我们不应该用auto_ptr来管理一个数组指针。
3)构造函数的explicit关键字有效阻止从一个“裸”指针隐式转换成auto_ptr;
2. 拷贝构造与赋值
与引用计数型指针不同,auto_ptr要求其对“裸”指针的完全占有。也就是一个“裸”指针只能同一时刻只能被一个auto_ptr拥有,所以在拷贝构造或者赋值操作时,要做的事所有权的转移。即就是使源对象失去所有权,所以拷贝构造函数和赋值函数的参数为引用而不是常引用。当然,因为一个auto_ptr同一个时刻只能指向一个裸指针,所以应该先释放原来拥有的对象。
弃用原因
1)因为一个auto_ptr被拷贝或者赋值后,失去了对原对象的所有权,这个时候,对这个auto_ptr的提领操作是不安全的。如下:
int *p=new int(0);
auto_ptr<int> ap1(p);
auto_ptr<int> ap2=ap1;
cout<<*ap1;//错误,此时ap1已经失去对p指针的拥有权
还有一种较为隐蔽的情形,如下:
void fun(auto_ptr<int> ap)
{
cout<<*ap;
}
auto_ptr<int> ap1(new int(0));
fun(ap1);
cout<<*ap1;//错误,调用fun函数的时候将其对象的拥有权转移给函数的形参ap,所以此时ap1不再拥有对象
该情况太隐蔽,将auto_ptr作为函数参数按值传递一定是要避免的,因为无法预料函数会对auto_ptr做什么,如果在该过程中失去了对对象的拥有权,这可能导致致命的执行期错误。
2)可以看到拷贝构造函数和赋值函数都提供了一个成员模板在不覆盖“正统”版本的情况下实现auto_ptr的隐式转换,如下:
class Object{};
class Base:public Object{};
那么下面代码就可以实现从auto_ptr到auto_ptr的隐式转换。
auto_ptr<Object> obj=auto_ptr<Base>(new Base);
3)因为auto_ptr不具有值语义,所以auto_ptr不能被用在stl标准容器中。
举例如下:
vector<auto_ptr<Object>> vec;
vec.push_back(auto_ptr<Object>(new Object(10)));
vec.push_back(auto_ptr<Object>(new Object(20)));
auto_ptr<Object> apobj=vec.back();//将vec里面最后一个指针的拥有权转移给apobj,即此时20所在的指针的拥有权转移到apobj指针
vec.size();//此时vec的大小仍然为2
for(auto &x:vec)
{
(*x).Print();//打印20的时候会报错
x->Print;
}
- 提领操作
提领操作有两个操作,一个是返回其所拥有的对象的引用,另一个是实现通过auto_ptr调用其所拥有对象的成员,如下:
class Base
{
void fun(){};
}
auto_ptr<A> ap(new Base());
(*ap).fun();
ap->fun();
首先我们要确保这个智能指针确实拥有某个对象,否则,这个操作的行为即对空指针的提领是未定义的。
使用auto_ptr的注意事项
- auto_ptr不能指向数组
- auto_ptr不能共享所有权
- auto_ptr不能通过赋值操作来初始化
- auto_ptr不能作为容器来使用
- auto_ptr不能作为容器的成员
- 不能把一个原生指针给两个智能指针对象管理