关于内存泄漏---auto_ptr
来源:互联网 发布:临沂蓝狐网络怎么样 编辑:程序博客网 时间:2024/06/10 23:22
上一篇,自己写了一些代码,在调试的时候去检测内存泄漏,现在来分析一下auto_ptr
源码是vs2015里的。ok,let's go !
template<class _Ty>struct auto_ptr_ref//引用指针{explicit auto_ptr_ref(_Ty *_Right)//只能显示调用,用来初始化成员变量: _Ref(_Right){}_Ty *_Ref;//成员变量,是一个_Ty类型的指针};
template<class _Ty>class auto_ptr;//声明模板类,auto_ptr
template<class _Ty>class auto_ptr{// wrap an object pointer to ensure destructionpublic:typedef auto_ptr<_Ty> _Myt;//_Myt ==> my typetypedef _Ty element_type;explicit auto_ptr(_Ty *_Ptr = 0) _THROW0()//必须显示调用,初始化成员变量_Myptr, : _Myptr(_Ptr)// auto_ptr<std::string>(new std::string()) one; {// construct from object pointer// one = new std::string(); 非法}// one = auto_ptr<std::string>(new std::string()); 显示调用auto_ptr(_Myt& _Right) _THROW0()//this._Myptr = _Right._Myptr; _Right._Myptr = 0;: _Myptr(_Right.release()){// construct by assuming pointer from _Right auto_ptr}auto_ptr(auto_ptr_ref<_Ty> _Right) _THROW0(){// construct by assuming pointer from _Right auto_ptr_ref_Ty *_Ptr = _Right._Ref;_Right._Ref = 0;// release old_Myptr = _Ptr;// reset this}template<class _Other>operator auto_ptr<_Other>() _THROW0(){// convert to compatible auto_ptrreturn (auto_ptr<_Other>(*this));}template<class _Other>operator auto_ptr_ref<_Other>() _THROW0(){// convert to compatible auto_ptr_ref_Other *_Cvtptr = _Myptr;// test implicit conversionauto_ptr_ref<_Other> _Ans(_Cvtptr);_Myptr = 0;// pass ownership to auto_ptr_refreturn (_Ans);}template<class _Other>_Myt& operator=(auto_ptr<_Other>& _Right) _THROW0(){// assign compatible _Right (assume pointer)reset(_Right.release());return (*this);}template<class _Other>auto_ptr(auto_ptr<_Other>& _Right) _THROW0(): _Myptr(_Right.release()){// construct by assuming pointer from _Right}_Myt& operator=(_Myt& _Right) _THROW0(){// assign compatible _Right (assume pointer)reset(_Right.release());return (*this);}_Myt& operator=(auto_ptr_ref<_Ty> _Right) _THROW0(){// assign compatible _Right._Ref (assume pointer)_Ty *_Ptr = _Right._Ref;_Right._Ref = 0;// release oldreset(_Ptr);// set newreturn (*this);}~auto_ptr() _NOEXCEPT{// destroy the objectdelete _Myptr;}_Ty& operator*() const _THROW0(){// return designated valuereturn (*get());}_Ty *operator->() const _THROW0(){// return pointer to class objectreturn (get());}_Ty *get() const _THROW0(){// return wrapped pointerreturn (_Myptr);}_Ty *release() _THROW0(){// return wrapped pointer and give up ownership_Ty *_Tmp = _Myptr;_Myptr = 0;return (_Tmp);}void reset(_Ty *_Ptr = 0){// destroy designated object and store new pointerif (_Ptr != _Myptr)delete _Myptr;_Myptr = _Ptr;}private:_Ty *_Myptr;// the wrapped object pointer};
源码如上,说实在,看了源码就很容易理解了,好我们用例子,和反汇编来分析
先写一个测试类
为了便于后面的反汇编分析,我们先来分析一下test类
test类的内存结构,无虚函数,无变量,所以test将占用1字节内存。
inline void printfInt(int i){printf("%d\n", i);}void main(){printfInt(sizeof(test));test* pTest = new test();pTest->testFunc();}反汇编:
test* pTest = new test();00C61080 push 1 00C61082 call operator new (0C610B5h) 00C61087 push offset string "test()\n" (0C62190h) 00C6108C call printf (0C61040h)//new test()
test类的各个函数的返汇编代码:
void main(){ test l_test; l_test.testFunc(); //l_test.~test(); 局部变量最后会被析构 }
test::test() 函数的反汇编,经过编译器的优化,直接调用 printf("test()\n");
test l_test;00BC1070 push offset string "test()\n" (0BC2118h) 00BC1075 call printf (0BC1040h)
test::testFunc() 也是直接调用 printf("test::testFunc()\n");
l_test.testFunc();00BC107A push offset string "test::testFunc()\n" (0BC212Ch) 00BC107F call printf (0BC1040h)
test::~test() 也是直接俄调用 printf("test::~test()\n");
//l_test.~test(); 局部变量最后会被析构 } 00BC1084 push offset string "~test()\n" (0BC2120h) 00BC1089 call printf (0BC1040h)
现在一个一个函数 来剖解 auto_ptr 类;
存储用内存来存,对这些内存的操作用函数。别跟我说,你用的是 c++,java,php等等。
首先,我们需要知道 一个auto_ptr 对象,会占用多大的内存,没有虚函数,一个指针变量,32为系统,当然就是4字节了,你不信吗? 上代码
inline void printfInt(int i){ printf("%d\n", i);}void main(){ printfInt(sizeof(std::auto_ptr<char>)); //输出 4}
template<class _Ty>struct auto_ptr_ref{// proxy reference for auto_ptr copyingexplicit auto_ptr_ref(_Ty *_Right): _Ref(_Right){// construct from generic pointer to auto_ptr ptr}_Ty *_Ref;// generic pointer to auto_ptr ptr};一个指针变量,占用4字节。
ok,我们来分析 auto_ptr类的 函数吧。
explicit auto_ptr(_Ty *_Ptr = 0) _THROW0() : _Myptr(_Ptr) { // construct from object pointer }
_Myptr 是 _Ty* 类型的私有变量,这个函数必须显示调用。无参的时候,_Myptr将被置为0;
我们来测试一下,测试代码:
template<class _Ty>inline void printfAddr(_Ty* addr){printf("%d\n", reinterpret_cast<int>(addr));}void main(){ typedef std::auto_ptr<test> test_t; test_t ap_test; //_Myptr = 0; printfAddr(ap_test.get()); // 输出 0 值 printfAddr(test_t(new test()).get()); //输出非 0 值}
ok 我们来看一下反汇编(release)
经过编译器优化 直接 执行_Myptr = 0 ,没有压栈弹栈操作。 printfAddr(ap_test.get()) 直接优化成 printf("%d\n",_Myptr);
test_t ap_test;//_Myptr = 0;01271099 mov dword ptr [ebp-14h],0 printfAddr(ap_test.get());// 输出 0 值012710A0 push 0 012710A2 push offset string "%d\n" (012721A4h)
printfAddr(test_t(new test()).get());//输出非 0 值012710B3 push 1 012710B5 call operator new (0127113Fh)012710BA mov esi,eax //分配test类内存。012710BC push offset string "test()\n" (01272190h) 012710C1 mov dword ptr [ap_test],esi //保存分配的 test* 指针012710C4 call printf (01271040h) //调用test构造函数012710C9 push esi 012710CA push offset string "%d\n" (012721A4h) 012710CF call printf (01271040h)//printf("%d\n",_Myptr)
ok 我们来看一下auto_ptr 的析构函数 和 get()函数
~auto_ptr() _NOEXCEPT{// destroy the objectdelete _Myptr;//直接删除对象}
_Ty *get() const _THROW0() { // return wrapped pointer return (_Myptr);//直接返回 对象指针 }
思考,其中的bug ,来上代码:
template<class _Ty>inline void printfAddr(_Ty* addr){printf("%d\n", reinterpret_cast<int>(addr));}template<class _Ty>void auto_ptr_bug(_Ty* pTy){_Ty* l_pTy = std::auto_ptr<_Ty>(pTy).get();printfAddr(l_pTy);}void main(){test* pTest = new test();auto_ptr_bug(pTest);delete pTest;//良好的编程风格喔,竟然出bug}
为什么,会出问题尼,因为 auto_ptr_bug函数里使用 std::auto_ptr,已经把 pTest对象delete了,所以最后delete pTest 就出问题了
所以我们需要改写 explicit auto_ptr(_Ty *_Ptr = 0) 构造函数,_Ptr只能为右值,不能为左值,因为左值,可能还被其他引用。
我们再来看另一种bug
void main(){typedef std::auto_ptr<std::string> ap_string_t;ap_string_t pString = ap_string_t(new std::string("string"));ap_string_t pString1 = pString;// operator= 参数应该为右引用printf(pString->c_str());//pString 指针被置空了,所以出现bug了}
看auto_ptr使用不当,会带来很多的问题,本来想避免内存泄漏的问题,却在使用不当的情况下,引入了更多的bug,但auto_ptr管理内存还是很高效的,关键要注意的是,我们不能把生存时间比auto_ptr变量长的指针变量作为auto_ptr构造函数的参数,所以要限制指针变量为右值,不能把 为左值的auto_ptr变量赋值给其他的auto_ptr变量.
ok ! 接着我们来分析一下 auto_ptr的兼容性
template<class _Other>operator auto_ptr<_Other>() _THROW0(){// convert to compatible auto_ptrreturn (auto_ptr<_Other>(*this));}template<class _Other>_Myt& operator=(auto_ptr<_Other>& _Right) _THROW0(){// assign compatible _Right (assume pointer)reset(_Right.release());return (*this);}template<class _Other>auto_ptr(auto_ptr<_Other>& _Right) _THROW0(): _Myptr(_Right.release()){// construct by assuming pointer from _Right}
上面这几个函数就是为了兼容性而写的,比如
可以这么写,auto_ptr也是可以的。
#include <memory>#include <string>#include <stdio.h>class parent{public: virtual void name() { printf("parent\n"); }};class son :public parent{public: void name() override { printf("son\n"); }};void main(){ typedef parent* parent_ptr; typedef son* son_ptr;parent_ptr pParent; son_ptr pSon = new son(); pParent = pSon; pSon = (son_ptr)pParent; //强制转换std::auto_ptr<parent> apParent; std::auto_ptr<son> apSon(new son()); std::auto_ptr<parent> apParent1(new son());apParent = apSon; //子类转换成父类,但apSon不能再被使用 apParent->name();// apSon->name(); 运行时出错 // apSon = apParent; 编译出错}
ok ! operator= 很简单,就不说了, 我们来看一下,最精华的部分. release() , ~auto_ptr()
_Ty *release() _THROW0(){// return wrapped pointer and give up ownership_Ty *_Tmp = _Myptr;_Myptr = 0;return (_Tmp);}~auto_ptr() _NOEXCEPT{// destroy the objectdelete _Myptr;}
void main(){std::auto_ptr<std::string> ap_string;}
上面的代码竟然没有bug, 对我而言,这是不可能的事,我觉得一定有bug,为什么呢,因为你,调用了auto_ptr(_Ty* rTy = 0) 的构造函数,那么_Myptr一定等于0,那么在析构函数被调用的时候。delete _Myptr 一定会出错啊(或许,一些高手已经看懂了,我为什么会这么认为的原因)。起初,我以为是release版本优化了,于是,我有换成debug版本,一步一步的跟踪,没错啊,是delete 0 啊,怎么不出错啊。卧槽。见鬼了。 说实在,不知什么时候开始,我习惯了 这样写代码。if(ptr != nullptr) { delete ptr;} 所以,我一直以为,delete 0 是错误的。最后带着疑惑,我百度了 delete 0 ,这不是一个错误的语句。
ok,我们看一下,release() 函数,都是被那些函数引用了
auto_ptr(_Myt& _Right) _THROW0(): _Myptr(_Right.release()){// construct by assuming pointer from _Right auto_ptr}template<class _Other>_Myt& operator=(auto_ptr<_Other>& _Right) _THROW0(){// assign compatible _Right (assume pointer)reset(_Right.release());return (*this);}template<class _Other>auto_ptr(auto_ptr<_Other>& _Right) _THROW0(): _Myptr(_Right.release()){// construct by assuming pointer from _Right}_Myt& operator=(_Myt& _Right) _THROW0(){// assign compatible _Right (assume pointer)reset(_Right.release());return (*this);}都是一些 ,以auto_ptr类型为参数的函数,进来的都是 auto_ptr类型的对象,这些对象把_Myptr 赋值给this._Myptr, 如果不用release 那么将有两个对象同时引用了指针变量,因为,这两个变量都是auto_ptr对象,最后都会调用,delete _Myptr.(两次) 第二次调用的时候,就是崩溃了。所以需要把 参数变量里的 _Myptr = 0;因为 delete 0 没有什么影响,所以。auto_ptr才能运行起来。
我最喜欢的一种释放宏:
#define Safdelete(ptr) if(nullptr != ptr) { delete ptr; ptr = nullptr;}
现在我终于明白了,真正的用意,是为了防止多次 delete ptr; 而不是 delete 0; 哇塞。
ok , 现在总结一下。
首先,我们不能把生存时间比auto_ptr变量长的指针变量作为auto_ptr构造函数的参数,所以要限制指针变量为右值,不能把 为左值的auto_ptr变量赋值给其他的auto_ptr变量.
其次,auto_ptr 不能使用数组指针,例如:std::auto_ptr<char>(new char[100]);
因此,我决定,修改一下auto_ptr的源码,更易于使用。
夜已深,我要去吃个面,去网吧玩几把英雄联盟了。以后再写修改版的auto_ptr. i just a 程序员 !!!
- 关于内存泄漏---auto_ptr
- 智能指针auto_ptr、内存泄漏解决
- 关于指针(内存)泄漏
- 关于查找内存泄漏
- 关于内存泄漏
- 关于Java内存泄漏
- 关于内存泄漏问题
- 关于colorWithPatternImage内存泄漏
- 关于内存泄漏
- 关于内存泄漏
- 关于内存泄漏
- 关于内存泄漏小记
- 关于内存泄漏
- 关于Java内存泄漏
- 关于内存泄漏
- 关于java内存泄漏
- 关于JS内存泄漏
- 关于内存泄漏
- Beginning Spring学习笔记——第3章(三)文件上传、异常处理和个性化
- 单例模式
- CORS——跨域请求那些事儿
- AVL 平衡二叉搜索树原理及编程实现 (C++)版本 第二版
- Android Saving Data
- 关于内存泄漏---auto_ptr
- loadrunner11 回放脚本Action.c(94): 错误 -27979: 找不到请求的表单 [MsgId: MERR-27979]
- LR回放https协议脚本失败:[GENERAL_MSG_CAT_SSL_ERROR]connect to host "XXX" failed:[10054] Connection reset by
- android MAGNETIC_FIELD
- Hadoop 2.0 data write operation acknowledgement
- sqlcipher加密已有数据库及其时机
- 北大方正暗偷明抢,是校办企业的反面教员
- 在国内最大的手机公司工作的那些事
- 浙江新再灵与华为云共同打造智慧城市电梯物联网