C++智能指针auto_ptr

C++98:auto_ptr

1.智能指针产生原因

众所周知,C++程序设计中使用堆内存是非常频繁的操作,堆内存的申请和释放都由程序员自己管理。然而,很多程序员在使用堆内存时都很头疼,因为一次不恰当的使用方法,很容易造成堆内存泄露(忘记释放),二次释放,程序发生异常时内存泄露等问题等。所以C++引入了智能指针,使用使用智能指针能更好的管理堆内存。

2.理解智能指针原理

1)智能指针是利用了一种叫做RAII技术对普通的指针进行封装,这使得智能指针实质是一个对象,行为表现的却像一个指针。

补充:RAII技术即资源分配及初始化,使用类来封装资源的分配和初始化,构造函数完成资源的分配和初始化,析构函数完成资源的清理,可以保证正确的初始化和资源释放。

2)智能指针是一个类,这个类的构造函数中传入一个普通指针,析构函数中释放传入的指针。智能指针的类都是栈上的对象,所以当函数(或程序)结束时会自动被释放。智能指针的作用是防止忘记调用delete释放内存和程序异常的进入catch块忘记释放内存。另外指针的释放时机也是非常有考究的,多次释放同一个指针会造成程序崩溃,这些都可以通过智能指针来解决。千万不要用一块非new分配的动态内存去初始化一个智能指针,因为自行释放非堆地址很有可能出现问题。

3)智能指针还有一个作用是把值语义转换成引用语义。

C++和Java有一处最大的区别在于语义不同,在Java里面,下列代码: 

Student s1 = new Student ();

Student s2 = s1;

 这里其实只生成了一个对象,s1和s2仅仅是共享对象的引用而已。但在C++中不是这样的,

  Student s1;

  Student s2 = s1;

  这里却是就是生成了两个对象。

3.包含头文件

C++98版的auto_ptr和C++11版的shared_ptr、unique_ptr、weak_ptr,都包含在头文件<memory>中,隶属于std命名空间中。

4.auto_ptr

C++的auto_ptr所做的事情,就是动态分配对象以及当对象不再需要时自动执行清理。

auto_ptr用法:

1)构造函数源码:

explicit auto_ptr(_Ty *_Ptr = 0) _THROW0()
: _Myptr(_Ptr)
{	// construct from object pointer
}

将指针ptr交给auto_ptr对象托管。 

2)拷贝构造源码:

typedef auto_ptr<_Ty> _Myt;

auto_ptr(_Myt& _Right) _THROW0()
: _Myptr(_Right.release())
{	// construct by assuming pointer from _Right auto_ptr
}

指针的托管权会发生转移。

3)赋值函数源码:

_Myt& operator=(_Myt& _Right) _THROW0()
{	// assign compatible _Right (assume pointer)
    reset(_Right.release());
    return (*this);
}

指针的托管权会发生转移。

4)析构函数源码:

~auto_ptr() _NOEXCEPT
{	// destroy the object
    delete _Myptr;
}

释放指针_Myptr指向的空间。

C++中对一个空指针NULL执行delete操作是安全的。所以在auto_ptr的析构函数中无须判断它所拥有指针是否为空。

4)_Ty* get(),获得auto_ptr所拥有的指针。

_Ty *get() const _THROW0()
{	// return wrapped pointer
    return (_Myptr);
}

5)_Ty* release(), 释放auto_ptr的所有权,并将所有用指针返回。

_Ty *release() _THROW0()
{	// return wrapped pointer and give up ownership
    _Ty *_Tmp = _Myptr;
    _Myptr = 0;
    return (_Tmp);
}

6)void reset(T* ptr=0), 接收所有权,接收之前拥有其它指针的话,必须先释放其空间。

void reset(_Ty *_Ptr = 0)
{	// destroy designated object and store new pointer
    if (_Ptr != _Myptr)
        delete _Myptr;
    _Myptr = _Ptr;
}

auto_ptr实现关键点

1)利用特点“栈上对象在离开作用范围时会自动析构”。

2)对于动态分配的内存,其作用范围是程序员手动控制的,这给程序员带来了方便但也不可避免疏忽造成的内存泄漏,毕竟只有编译器是最可靠的。

3)auto_ptr通过在栈上构建一个对象a,对象a中wrap了动态分配内存的指针p,所有对指针p的操作都转为对对象a的操作。而在a的析构函数中会自动释放p的空间,而该析构函数是编译器自动调用的,无需程序员操心。

注意:

1)auto_ptr不能共享所有权,即不要让两个auto_ptr指向同一个对象,虽然编译可以通过,容易出现两个对象分别离开作用域时,被delete两次。
2)auto_ptr对象被拷贝或者被赋值后,已经失去了对原指针的所有权,此时,对这个auto_ptr的读取操作是不安全的。

auto_ptr<int> p1(new int(1));
auto_ptr<int> p2(p1);
cout << *p1 << endl;

//and
auto_ptr<int> p3=p1;
cout << *p1 << endl;

这种情况较为隐蔽的情形出现在将auto_ptr作为函数参数按值传递,因为在函数调用过程中在函数的作用域中会产生一个局部的临时auto_ptr对象来接收传入的 auto_ptr(拷贝构造),这样,传入的实参auto_ptr的对其指针的所有权转移到了临时auto_ptr对象上,临时auto_ptr在函数退出时析构,所以当函数调用结束,原实参所指向的对象已经被删除了。

void func(auto_ptr<int> ap)
{
    cout << *ap << endl;
}

auto_ptr<int> ap(new int(1));
func(ap);
cout << *ap1 << endl;//错误,函数调用结束后,ap1已经不再拥有任何对象了

3)auto_ptr支持所拥有的指针类型之间的隐式类型转换。

class base{};
class derived: public base{};
//下列代码就可以通过,实现从auto_ptr<derived>到auto_ptr<base>的隐式转换,因为derived*可以转换成base*类型
auto_ptr<base> apbase = auto_ptr<derived>(new derived);

4)auto_ptr不能指向数组,因为auto_ptr在析构的时候只是调用delete,而数组应该要调用delete[]。

5)auto_ptr不能作为容器对象,C++的STL容器对于容器元素类型的要求是有值语义,即可以赋值和复制。auto_ptr在赋值和复制时都进行了特殊操作,所以auto_ptr对象不能作为STL容器元素。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值