C++ 智能指针Auto_PTR 分析

时间:2023-02-23 09:24:32

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&plus;&plus;智能指针&lpar;auto&lowbar;ptr&rpar;详解

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

  2. 自己动手实现智能指针auto&lowbar;ptr

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

  3. C&plus;&plus; 智能指针auto&lowbar;ptr

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

  4. 关于智能指针auto&lowbar;ptr

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

  5. C&plus;&plus;中的智能指针&lpar;auto&lowbar;ptr&rpar;

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

  6. 【C&plus;&plus;】智能指针auto&lowbar;ptr简单的实现

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

  7. 智能指针auto&lowbar;ptr &amp&semi; shared&lowbar;ptr

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

  8. C&plus;&plus;智能指针 auto&lowbar;ptr

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

  9. C&plus;&plus;智能指针--auto&lowbar;ptr指针

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

随机推荐

  1. Microsoft Dynamics AX 7 新特性探索 - Demo 部署&lpar;Part 1&rpar;

    Dynamics AX 7已经发布了一段时间了,我们知道这次微软为我们带来了许多令人激动的新特性.在这个系列里,Reinhard将揭开New Dynamics AX的神秘面纱,和大家一起探索这些新的特 ...

  2. asp&period;net linux 环境部署, jexus

    cd /tmpwget linuxdot.net/down/jexus-5.8.1-x64.tar.gztar -zxvf jexus-5.8.1-x64.tar.gzsudo mv jexus /u ...

  3. sqlserver sum 和count在关于进行统计时的区别

    sum是对内容的数量进行相加,count 对表行数 对象进行统计 在使用 case 时,如 select subject,count(case when score>80 then score ...

  4. oracle 基本语句练习&lpar;一&rpar; where&comma; between &comma;null&comma; like&comma;转义字符&comma;order by

    1   where  筛选关键字; select * from emp where job >'CLERK' 从emp表中列出 job值大于'CLERK'的项 (由于比较的是字符串,所以加单引号 ...

  5. C语言中的位段(位域)知识

    在结构体或类中,为了节省成员的存储空间,可以定义某些由位组成的字段,这些字段可以不需要以byte为单位. 这些不同位长度的字段实际存储于一个或多个整形变量.位段成员必须声明为int, signed i ...

  6. font-spider问题【已解决】

    最近写一个项目,使用了引入的字体,然而字体太大,于是找解决方法,想要把字体压缩一下,然后找到了font-spider;font-spider使用方法这里就不多说了,网上一大把,主要是在node里面安装 ...

  7. android checkBox setTextColor无效

    代码中动态设置checkBox的文字选中背景和未选中背景,用如下代码: checkView.setTextColor(getResources().getColor(R.color.item_colo ...

  8. C&num;如何打开一个窗体&comma;同时关闭该窗体

  9. Windows 下使用nginx命令启动

    http://wanganwu.blog.163.com/blog/static/7788722012322111417966/ Windows下Nginx的启动.停止等命令 在Windows下使用N ...

  10. Spring整合Disruptor

    原文:https://segmentfault.com/a/1190000014469173 什么是Disruptor 从功能上来看,Disruptor 是实现了“队列”的功能,而且是一个有界队列.那 ...