C++ 智能指针Auto_PTR 分析
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 分析的更多相关文章
- C++智能指针(auto_ptr)详解
智能指针(auto_ptr) 这个名字听起来很酷是不是?其实auto_ptr 只是C++标准库提供的一个类模板,它与传统的new/delete控制内存相比有一定优势,但也有其局限.本文总结的8个问题足 ...
- 自己动手实现智能指针auto_ptr
面试的时候,我们经常会被问到如何自己动手实现智能指针auto_ptr.今天我就一边参考STL库中的源代码,一边将auto_ptr的实现敲一遍. auto_ptr归根到底是一个模版类,那么这个类要实现哪 ...
- C++ 智能指针auto_ptr
template<class T> class auto_ptr { public: ); // Item M5 有“explicitfor”// 的描述 template<clas ...
- 关于智能指针auto_ptr
智能指针auto_ptr和shared_ptr也是面试中经常被问到的一个 感觉看auto_ptr的源码反而更加容易理解一些,因为源码的代码量并不大,而且比较容易理解. 本篇主要介绍auto_ptr 其 ...
- C++中的智能指针(auto_ptr)
实际上auto_ptr 仅仅是C++标准库提供的一个类模板,它与传统的new/delete控制内存相比有一定优势.使用它不必每次都手动调用delete去释放内存.当然有利也有弊,也不是全然完美的. 本 ...
- 【C++】智能指针auto_ptr简单的实现
//[C++]智能指针auto_ptr简单的实现 #include <iostream> using namespace std; template <class _Ty> c ...
- 智能指针auto_ptr & shared_ptr
转载:智能指针auto_ptr 很多人听说过标准auto_ptr智能指针机制,但并不是每个人都天天使用它.这真是个遗憾,因为auto_ptr优雅地解决了C++设计和编码中常见的问题,正确地使用它可以生 ...
- C++智能指针 auto_ptr
C++智能指针 auto_ptr auto_ptr 是一个轻量级的智能指针, 定义于 memory (非memory.h)中, 命名空间为 std. auto_ptr 适合用来管理生命周期比较短或者不 ...
- C++智能指针--auto_ptr指针
auto_ptr是C++标准库提供的类模板,头文件<memory>,auto_ptr对象通过初始化指向由new创建的动态内存,它是这块内存的拥有者,一块内存不能同一时候被分给两个拥有者.当 ...
随机推荐
- JVM内存模型及垃圾回收算法
国内私募机构九鼎控股打造APP,来就送 20元现金领取地址:http://jdb.jiudingcapital.com/phone.html内部邀请码:C8E245J (不写邀请码,没有现金送)国内私 ...
- 算法:图(Graph)的遍历、最小生成树和拓扑排序
背景 不同的数据结构有不同的用途,像:数组.链表.队列.栈多数是用来做为基本的工具使用,二叉树多用来作为已排序元素列表的存储,B 树用在存储中,本文介绍的 Graph 多数是为了解决现实问题(说到底, ...
- 乘法器的Verilog HDL实现
原文链接:http://www.cnblogs.com/shengansong/archive/2011/05/23/2054401.html 1. 串行乘法器 两个N位二进制数x.y的乘积用简单的 ...
- Saving HDU hdu
话说上回讲到海东集团面临内外交困.公司的元老也仅仅剩下XHD夫妇二人了.显然.作为多年拼搏的商人,XHD不会坐以待毙的. 一天,当他正在苦思冥想解困良策的时候.突然想到了自己的传家宝,那是公司成立的时 ...
- 批处理bat一键安装APK
批处理bat一键安装APK 2018年10月11日 10:48:28 xyzshenxiang 阅读数:77 在安装apk时,每次都得拷贝到手机内存中,然后在手机上操作安装这样做非常不方便,下面介 ...
- [转]C++之多态性与虚函数
面向对象程序设计中的多态性是指向不同的对象发送同一个消息,不同对象对应同一消息产生不同行为.在程序中消息就是调用函数,不同的行为就是指不同的实现方法,即执行不同的函数体.也可以这样说就是实现了“一个接 ...
- CI框架入门中的简单MVC样例
最简单的CI模型: 注意:模型须要用到数据库 配置文件在appcation/config.php 这里我们要用到数据库,须要将databases.php中的 相关參数填写一下,详细不再赘述. 直接进入 ...
- socket bind 随机端口
https://www.cprogramming.com/code_blocks/ 这个地址可以下载c, c++的编译器,在windows下可以用的 IDE. bind到端口0上,系统就会自动分配,但 ...
- Cognos让指定用户不具有删除内容的权限
为了方便用户使用Cognos,现在很多对权限要求不够严格的用户就想到了可以让用户实现匿名登陆,即不登陆系统即可实现访问报表,当然这也仅仅是按照客户的需求,我个人认为一个安全性的数据平台还是需要对登陆. ...
- 【Struts2学习笔记(9)】单文件上传和多文件上传
(1)单文件上传 第一步:在WEB-INF/lib下增加commons-fileupload-1.2.1.jar.commons-io-1.3.2.jar. 这两个文件能够从http://common ...