智能指针-----》auto_ptr

智能指针:
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的使用以及弃用原因

  1. 构造函数与析构函数
    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;
}
  1. 提领操作
    提领操作有两个操作,一个是返回其所拥有的对象的引用,另一个是实现通过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不能作为容器的成员
  • 不能把一个原生指针给两个智能指针对象管理
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值