{闲得慌}看auto_ptr模板类源码玩

 

    auto_ptr类实现了较为安全的指针。例如在这种情况下:

void foo() {
    int *ptr = new int (123);
    do_something(); // may cause memory leak...
    delete ptr;
} 

    如果在do_something()中抛出异常,且未被捕获,那么ptr指针无法被delete,造成内存泄漏。

利用C++提供的auto_ptr类改写之,则可以解决这个问题:

void foo() {
    auto_ptr<int> ap(new int (123));
    do_something(); // destructor will be called
} 

 

    那么auto_ptr是怎么实现的呢?打开memory头文件(这里以DevC++ 4.9.9.2版本中所带的g++编译器include目录下的memory头文件为例)一探究竟吧~

 

    auto_ptr类是一个类模板,模板形参_Tp,拥有一个private的指针,即用户想要交给其管理的指针。

其余成员包括各种构造函数/复制构造函数/赋值操作符:

explicit auto_ptr(element_type* __p = 0);

auto_ptr(auto_ptr& __a); // 注意参数不是常规的const引用,稍候详谈

template<typename _Tp1>
     auto_ptr(auto_ptr<_Tp1>& __a);

auto_ptr(auto_ptr_ref<element_type> __ref);

~auto_ptr();

auto_ptr& operator=(auto_ptr& __a);

template<typename _Tp1> 
    auto_ptr& operator=(auto_ptr<_Tp1>& __a);

auto_ptr& operator=(auto_ptr_ref<element_type> __ref);

auto_ptr对外提供的接口:

element_type& operator*() const;

element_type* operator->() const;

element_type* get() const;

element_type* release();

void reset(element_type* __p = 0);

还有两个类型转换操作符:

template<typename _Tp1>
    operator auto_ptr_ref<_Tp1>();

template<typename _Tp1>
    operator auto_ptr<_Tp1>()

 

    大部分代码都比较简单易懂,只需要注意auto_ptr类实现的赋值和复制行为与通常不同,完成对目标对象的赋值/复制操作的同时,源对象会成为未绑定的auto_ptr。

 

    比较引人关注的是那个非const引用的复制构造函数。试想有这样的函数:

template<class T>
    void foo(auto_ptr<T>);

template<class T>
    auto_ptr<T> bar(void);

要实现这样的调用:

foo(auto_ptr<int>(new int (123)));
auto_ptr<int> ap = bar();
 

那么传给foo()的参数是一个临时对象,从bar()返回的参数也是临时对象。传参的时候会调用复制构造函数,而从bar()返回的时候会调用赋值操作符。两者情况类似,让我们看看复制构造函数中发生的事情。

    由于临时对象是右值,所以复制构造函数的参数应该是const引用。但是,如果是const引用的话,就没办法修改源对象持有的指针了。对了,我们还有mutable关键字。如果给指针加上了mutable的修饰,倒是可以修改了,可问题是,如果源对象确实是一个const的auto_ptr怎么办呢?

    来看看大师们的设计:将复制构造函数的参数设置为non-const引用,增加一个新的辅助类auto_ptr_ref

其定义如下:

 /**
   *  A wrapper class to provide auto_ptr with reference semantics.
   *  For example, an auto_ptr can be assigned (or constructed from)
   *  the result of a function which returns an auto_ptr by value.
   *
   *  All the auto_ptr_ref stuff should happen behind the scenes.
   */
  template<typename _Tp1>
    struct auto_ptr_ref
    {
      _Tp1* _M_ptr;
      
      explicit
      auto_ptr_ref(_Tp1* __p): _M_ptr(__p) { }
    };

这样的设计要实现的目标是,从一个右值构造出auto_ptr对象。

    那么这个辅助类是怎样工作的呢?总的来说是两步转换,从auto_ptr到auto_ptr_ref,再到auto_ptr。还是以复制构造函数为例。在前面对foo()函数的调用中,临时对象是auto_ptr类类型,自然不会调用接受non-const引用参数的复制构造函数。那么唯一可能进行的只能是从auto_ptr_ref类进行构造了。现在那个神奇的转换操作符就起作用了:

template<typename _Tp1>
    operator auto_ptr_ref<_Tp1>() throw() { 
        return auto_ptr_ref<_Tp1>(this->release()); 
    }

在转换时,将原来的auto_ptr置成未绑定的对象,用它持有的指针构造了一个auto_ptr_ref对象。接着再用这个auto_ptr_ref对象构造出一个新的auto_ptr对象,把这个新对象传给foo()函数。

    为了验证一下这个过程,修改一下memory头文件中的代码,在以下位置加上printf语句:

1. 上述的转换操作符

2. 用auto_ptr_ref类的构造函数

3. auto_ptr的析构函数

4. auto_ptr_ref的析构函数(自己加上一个析构函数定义)

然后运行这段程序:

#include <iostream>
#include <memory>
using namespace std;

template <class T>
void foo(auto_ptr<T> a) {
    cout << *a << endl;
}

int main()
{
    foo(auto_ptr<int>(new int (123)));
 
    return 0;
}

输出如下:

convert to auto_ptr_ref
construct an auto_ptr from auto_ptr_ref
123
destroy an auto_ptr
destroy an auto_ptr_ref
destroy an auto_ptr
 

析构的三个对象中,有临时对象、新构造出的auto_ptr对象和看不见的auto_ptr_ref对象。对于赋值操作符,也可以用相同的方法跟踪一下执行流程。只不过它调用的是参数为auto_ptr_ref的赋值操作符。

 

    这样一来便解决了foo()和bar()函数的问题。主要根源还是在于auto_ptr类与众不同的复制和赋值行为。

 

    OK~就写到这儿~累死人啊……第一篇啊……纯属菜鸟胡言,欢迎众大师拍砖,不喜者请绕道……EOF

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值