C++的动态内存的分配与释放是个挺折磨人的事情,尤其异常分支复杂时(比如一堆try catch中,各catch里需要做delete 掉相关的堆上分配的内存),极有可能产生内存泄露的情况。C++中提供了智能指针作为可选的解决方案, C++标准库中自带的智能指针是auto_ptr,它在大多数场景下是满足需求的。针对auto_ptr的缺点,boost和loki两套库都扩展出一些智能指针,并且boost中有两位幸运儿入选了tr1中(std::tr1::shared_ptr,std::tr1::weak_ptr)。本文就gcc中auto_ptr的实现做些分析,以飨自己。笔记采用注释源码的方式。

/**
* 这个wrapper类提供auto_ptr以引用语义,在下面的操作中有介绍。
*/

template<typename _Tp1>

struct auto_ptr_ref {

_Tp1* _M_ptr;

explicit auto_ptr_ref(_Tp1* __p) :

_M_ptr(__p) {

}

};

/**

* auto_ptr的实现还是很简单的,使用上也简单。在创建auto_ptr对象后,

* 通常的使用也就是调用它的*和->操作符,如下面的sample片段:

* AutoPtr<Admin> ptr1(new Admin());

* cout<<ptr1->getAge()<<endl;

* cout<<”obj:”<<*ptr1<<endl;

* 可以看到,auto_ptr中的成员函数都是throw()不抛异常的。

*/

template<typename _Tp>

class auto_ptr {

private:

_Tp* _M_ptr;//

public:

/// The pointed-to type.

typedef _Tp element_type;

/**
* 构造函数,将auto_ptr绑定到指针__p。
* __p是一个指向new出来的对象的指针,默认为0(NULL)是说auto_ptr的构造函数可以不传参构造,
* 这时成员_M_ptr=0,如果接着解引用auto_ptr对象,将Segmentation fault。当然,通常应用auto_ptr的构造
* 函数会传参的。auto_ptr提供了get函数来判断_M_ptr是否为空、reset函数重置_M_ptr指针。
* 在继承情况下,_M_ptr可以是__p的基类型。
* 构造函数声明为explicit表示禁止参数的自动类型转换(因为它们总是邪恶的)。
*
*/

explicit auto_ptr(element_type* __p = 0) throw () :

_M_ptr(__p) {

}

/**
* 辅助函数
*/

element_type*     release() throw () {

element_type* __tmp = _M_ptr;

_M_ptr = 0;

return __tmp;

}

/**
* auto_ptr的复制构造函数是很邪恶的,它的逻辑是将参数__a中的指针挪给新对象的,
* 原来的__a的内置指针被置空,接下来就不能继续操作__a引用的对象,否则就掉进出错的陷阱。
* 另外,复制构造函数的参数不是个const,因为它需要修改参数内容的。
*/

auto_ptr(auto_ptr& __a) throw () :

_M_ptr(__a.release()) {

}

/**
* 成员函数模板。好吧,我承认,我对模板也是半知半解(注意,不是一知半解)。这个函数
* 用于将继承体系中的子类型上溯成基类型。举个例子:
* 假如User是基类,Admin是派生类,那么下面的操作是ok的。
* auto_ptr<Admin> ptr2(new Admin());
* auto_ptr<User> ptr3(ptr2);
* 编译器的原理类型识别和转换的大致过程是:当模板参数类型不匹配时(_Tp1转成_Tp),
* 编译首先检查是否存在合适的类型转换操作符(auto_ptr是没有的),如果没有则检查是否
* 存在合适的成员函数模板完成类型转换。在_Tp1是_Tp的派生类的情况下,这种转换就会成功。
* 这也是说,编译器检查的是模板参数类型而不是对象的实际类型,所以,下面的例子编译就会失败:
* auto_ptr<User> ptr2(new Admin());
* auto_ptr<Admin> ptr3(ptr2);
*/

template<typename _Tp1> auto_ptr(auto_ptr<_Tp1>& __a) throw () :

_M_ptr(__a.release()) {

}

/**
* @brief 重置管理的对象指针,如果_M_ptr不为空,会delete掉。如果重置的指针就是本身
* 的_M_ptr,就是个空操作。
* @param __p 对象指针.
*/

void reset(element_type* __p = 0) throw () {

if (__p != _M_ptr) {

delete _M_ptr;

_M_ptr = __p;

}

}

/**
* 赋值操作符,和复制构造函数一样是邪恶的,赋值操作会delete掉右值管理的对象指针。
* 如果auto_ptr对象作为函数参数传递,并且是传值,那么这个调用过程会涉及到赋值操作符
* 的调用,产生并不期待的delete外部对象的结果。
*/

auto_ptr& operator=(auto_ptr& __a) throw () {

reset(__a.release());

return *this;

}

/**
* 成员函数模板赋值操作符
*/

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

reset(__a.release());

return *this;

}

/**
* 析构函数,操作很明显,因为是delete,所以auto_ptr是不支持数组指针的,否则会有
* 内存泄露。
*/

~auto_ptr() {

delete _M_ptr;

}

/**
* 解引用操作符
*/

element_type& operator*() const throw () {

_GLIBCXX_DEBUG_ASSERT(_M_ptr != 0);

return *_M_ptr;

}

/**
* ->操作符,是auto_ptr被用得最多的调用吧。
*/

element_type* operator->() const throw () {

_GLIBCXX_DEBUG_ASSERT(_M_ptr != 0);

return _M_ptr;

}

/**
* 返回auto_ptr管理的指针,这通常用于判断指针是否为空的情况,所以,如果要判断
* auto_ptr管理的指针是否为空,不要使用if(auto_ptr_obj){}而是使用get函数(实际上,
* 因为auto_ptr并没用定义指向element_type的dumb指针的隐式类型转换操作符,所以根本
* 编译不过if(auto_ptr_obj))。
* 但是,auto_ptr并没有禁止你进一步操作你得到的指针,甚至delete它使
* auto_ptr对象内置的指针悬空。
*/

element_type* get() const throw () {

return _M_ptr;

}

/**
* 下面的三个函数连带auto_ptr_ref是auto_ptr中的神奇之笔,因为我拍了好多次脑袋才想明白
* 是怎么的应用原理。考虑两种使用情况:
* 1)void foo(auto_ptr< User> ptr);
* 2)auto_ptr< User> func_returning_auto_ptr(…..);
* auto_ptr ptr< User> = func_returning_auto_ptr(…..);
* 对于第一种情况,当调用方式是:foo(auto_ptr< User>(new User));时,因为是传值调用,
* 而实参是个临时对象,所以需要做赋值构造对象,但auto_ptr的赋值构造函数参数并不是const的
* 所以不匹配其复制构造函数。auto_ptr采用了曲线策略,编译器接着检查类型转换操作符,
* 发现operator auto_ptr_ref<_Tp1>()是匹配的,所以将临时对象转成auto_ptr_ref,再调用
* auto_ptr(auto_ptr_ref __ref)把auto_ptr_ref转成auto_ptr。
*/

auto_ptr(auto_ptr_ref<element_type> __ref) throw () :

_M_ptr(__ref._M_ptr) {

}

template<typename _Tp1> operator auto_ptr_ref<_Tp1>() throw () {

return auto_ptr_ref<_Tp1> (this->release());

}

/**
* 和auto_ptr(auto_ptr_ref __ref)相似
*/

auto_ptr& operator=(auto_ptr_ref<element_type> __ref) throw () {

if (__ref._M_ptr != this->get()) {

delete _M_ptr;

_M_ptr = __ref._M_ptr;

}

return *this;

}

/**
* 怎么说这个函数呢?我还不晓得这个转换操作符在什么时候调用呢?
*/

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

return auto_ptr<_Tp1> (this->release());

}

};

//auto_ptr是不支持void类型的模板特化。

template<> class auto_ptr<void> {

public:

typedef void element_type;

};

C++ 智能指针Auto_PTR 分析的更多相关文章

  1. C++智能指针(auto_ptr)详解

    智能指针(auto_ptr) 这个名字听起来很酷是不是?其实auto_ptr 只是C++标准库提供的一个类模板,它与传统的new/delete控制内存相比有一定优势,但也有其局限.本文总结的8个问题足 ...

  2. 自己动手实现智能指针auto_ptr

    面试的时候,我们经常会被问到如何自己动手实现智能指针auto_ptr.今天我就一边参考STL库中的源代码,一边将auto_ptr的实现敲一遍. auto_ptr归根到底是一个模版类,那么这个类要实现哪 ...

  3. C++ 智能指针auto_ptr

    template<class T> class auto_ptr { public: ); // Item M5 有“explicitfor”// 的描述 template<clas ...

  4. 关于智能指针auto_ptr

    智能指针auto_ptr和shared_ptr也是面试中经常被问到的一个 感觉看auto_ptr的源码反而更加容易理解一些,因为源码的代码量并不大,而且比较容易理解. 本篇主要介绍auto_ptr 其 ...

  5. C++中的智能指针(auto_ptr)

    实际上auto_ptr 仅仅是C++标准库提供的一个类模板,它与传统的new/delete控制内存相比有一定优势.使用它不必每次都手动调用delete去释放内存.当然有利也有弊,也不是全然完美的. 本 ...

  6. 【C++】智能指针auto_ptr简单的实现

    //[C++]智能指针auto_ptr简单的实现 #include <iostream> using namespace std; template <class _Ty> c ...

  7. 智能指针auto_ptr & shared_ptr

    转载:智能指针auto_ptr 很多人听说过标准auto_ptr智能指针机制,但并不是每个人都天天使用它.这真是个遗憾,因为auto_ptr优雅地解决了C++设计和编码中常见的问题,正确地使用它可以生 ...

  8. C++智能指针 auto_ptr

    C++智能指针 auto_ptr auto_ptr 是一个轻量级的智能指针, 定义于 memory (非memory.h)中, 命名空间为 std. auto_ptr 适合用来管理生命周期比较短或者不 ...

  9. C++智能指针--auto_ptr指针

    auto_ptr是C++标准库提供的类模板,头文件<memory>,auto_ptr对象通过初始化指向由new创建的动态内存,它是这块内存的拥有者,一块内存不能同一时候被分给两个拥有者.当 ...

随机推荐

  1. extjs 动态设定 DateField 最大值 最小值

    yxrqDate.minValue = new Date();yxrqDate.maxValue = new Date(9000,1,1);yxrqDate.validate(); //var pic ...

  2. iOS 下 Podfile 使用方法

    配置 Podlist Pod 是 iOS 下包管理工具,类似于 JavaScript 里的 npm 或 yarn. 创建 Podfile 创建 Podfile 有两种方式: 打开 Terminal,在 ...

  3. copy and paste ,做到这样也很牛逼了

    db笔记本 mysql资源 mysql5.1中文参考手册 mysql管理 基于linux使用mysql二进制包安装mysql5.5 mysql client命令行选项 mysqld服务器系统变量和状态 ...

  4. android之截屏(包括截取scrollview与listview的)

    public class ScreenShot { // 获取指定Activity的截屏,保存到png文件 public static Bitmap takeScreenShot(Activity a ...

  5. 进程操作篇atexit execl exit fprintf fscanf getpid nice get priority printf setpid system vfork wait waitpid

    atexit(设置程序正常结束前调用的函数) 相关函数 _exit,exit,on_exit 表头文件 #include<stdlib.h> 定义函数 int atexit (void ( ...

  6. Android ToggleButton Example--开关按钮

    Android ToggleButton Example 在 Android 中,  “android.widget.ToggleButton” 是个特殊的类,可以渲染出一个“开关按钮” ,顾名思义, ...

  7. iOS开发-委托实战

    昨天晚上头疼,写了一部分草草的收笔了,早上起来补发一篇文章,昨天关于委托的基本使用和概念都稍微讲了一下,最开始学习委托的时候苹果官网和中文的博客文章看了不少,相似指数比较高.委托在命名要准确,最好是一 ...

  8. js中replace的用法

    replace方法的语法是:stringObj.replace(rgExp, replaceText) 其中stringObj是字符串(string),reExp可以是正则表达式对象(RegExp)也 ...

  9. Shape of HDU_hdu_2108(几何).java

    Shape of HDU Time Limit: 3000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) Tot ...

  10. 【转】Understanding the Angular Boot Process

    原文: https://medium.com/@coderonfleek/understanding-the-angular-boot-process-9a338b06248c ----------- ...