c++ 排查内存泄漏的妙招
前言
对于c++而言,如何查找内存泄漏是程序员亘古不变的话题;解决之道可谓花样繁多。因为最近要用到QT写程序,摆在我面前的第一个重要问题是内存防泄漏。如果能找到一个简单而行之有效的方法,对后续开发大有裨益。久思终得诀窍,本文就详细介绍我对此问题的应对之策。(文末符完整代码)
如何判断内存有泄漏
内存分配和释放对应的操作是new、delete。如何判断内存是否释放干净?其实判断起来非常简单:一个独立的模块整个生存周期内new的个数和delete的个数相等。用伪代码标示如下:
intnewCount=0; intdeleteCount=0; //new操作时 newclass(); newCount++; //delete操作时 delete*objPtr; deleteCount++; //模块结束时 if(newCount!=deleteCount) { 内存有泄漏 }
如果对所有的new和delete操作,加上如上几行代码,就能发现是否有内存泄漏问题。如果采用上面方法解决问题,手段太low了。
我们的方法有如下特点:
1使用起来超级简单,不增加开发难度。
2发生内存泄漏时,能定位到具体是哪个类。
托管newdelete操作符
要跟踪所有的new、delete操作,最简单的办法就是托管new、delete。不直接调用系统的操作符,而是用我们自己写的函数处理。在我们的函数内部,则别有洞天;对new和delete的跟踪和记录就为我所欲也。托管new和delete需用到模板函数,代码如下:
classMemManage { //单实例模式 private: staticMemManage*_instance_ptr; public: staticMemManage*instance() { if(_instance_ptr==nullptr) { _instance_ptr=newMemManage(); } return_instance_ptr; } public: MemManage(); //new操作构造函数没有参数 templateT*New() { ShowOperationMessage (true); returnnewT(); }; //new操作构造函数有1个参数 template T*New(TParam1param) { ShowOperationMessage (true); returnnewT(param); }; //new操作构造函数有2个参数 template T*New(TParam1param1,TParam2param2) { ShowOperationMessage (true); returnnewT(param1,param2); }; //delete操作 template voidDelete(Tt) { if(t==nullptr) return; ShowOperationMessage (false); deletet; }; //记录newdelete template voidShowOperationMessage(boolisNew) { //操作符对应的类名称 consttype_info&nInfo=typeid(T); QStringclassName=nInfo.name(); if(isNew) { _newCount++; } else { _deleteCount++; } if(!_showDetailMessage) { return; } if(isNew) { qDebug()<<"*New"< 如何使用辅助类
使用起来很简单,示例代码如下:
//*****new和delete使用伪代码 //new操作,需根据构造函数的参数个数调用对应的函数 //构造函数没有参数 QFile*file=MemManage::instance()->New(); //构造函数有1个参数 QFile*file=MemManage::instance()->New ("filename"); //构造函数有2个参数 QFile*file=MemManage::instance()->New ("filename",true); //delete只有一种形式 MemManage::instance()->Delete(file); 一个模块调用周期结束调用下列代码,查看是否有内存泄漏:
voidShowNewDelete(boolisShowDetail) { intleftNew=_newCount-_deleteCount; qDebug()<<"***********************"; qDebug()<<"totalNew:"<<_newCount<<"Delete:"<<_deleteCount<<"leftNew:"<ShowNewDelete(true); //debug输出如下,如果leftNew为0,则没内存泄漏 totalNew:166Delete:6leftNew:160 进一步定位内存泄漏问题
通过判断new和delete的个数是否相等,只是知道了是否有内存泄漏;进一步定位问题,才能方便我们解决问题。如果能定位到操作哪一个类时,发生了内存泄漏,则问题范围就大大缩小。我们可以按类名,记录new和delete操作个数,c++获取类名函数如下:
consttype_info&nInfo=typeid(T); QStringclassName=nInfo.name();建立一个map表,记录类名对应的操作信息:
//每个类统计的信息 classMemObjInfo { public: intNewCount=0; intDeletCount=0; QStringClassName; }; //map对照表 QMap_mapMemObjCount; //按类名统计 voidAddCount(QString&className,boolisNew) { QMap ::ConstIteratori=_mapMemObjCount.find(className); if(i==_mapMemObjCount.constEnd()) { MemObjInfo*info=newMemObjInfo(); info->ClassName=className; if(isNew) { info->NewCount++; } else { info->DeletCount++; } _mapMemObjCount.insert(className,info); } else { MemObjInfo*info=i.value(); if(isNew) { info->NewCount++; } else { info->DeletCount++; } } } 如果有内存泄漏则会输出如下信息:
如上图,对5个类的操作发送了内存泄漏。比如我们知道了类OfdDocumentPageAttr发生内存泄漏,就很容易定位问题了。
辅助类完整代码:
#ifndefMEMMANAGE_H #defineMEMMANAGE_H #include#include #include classLockRealse { public: LockRealse(QMutex*mutex) { _mutex=mutex; _mutex->lock(); } ~LockRealse() { _mutex->unlock(); } private: QMutex*_mutex; }; classMemObjInfo { public: intNewCount=0; intDeletCount=0; QStringClassName; }; classMemManage { private: staticMemManage*_instance_ptr; public: staticMemManage*instance() { if(_instance_ptr==nullptr) { _instance_ptr=newMemManage(); } return_instance_ptr; } public: MemManage() { _threadMutex=newQMutex(); _newCount=0; _deleteCount=0; } template T*New() { ShowOperationMessage (true); returnnewT(); }; template T*New(TParam1param) { ShowOperationMessage (true); returnnewT(param); }; template T*New(TParam1param1,TParam2param2) { ShowOperationMessage (true); returnnewT(param1,param2); }; template voidDelete(Tt) { if(t==nullptr) return; ShowOperationMessage (false); deletet; }; voidShowNewDelete(boolisShowDetail) { intleftNew=_newCount-_deleteCount; qDebug()<<"***********************"; qDebug()<<"totalNew:"<<_newCount<<"Delete:"<<_deleteCount<<"leftNew:"< voidclearAndDelete(QList &list) { foreach(Titem,list) { //Delete(item); } list.clear(); }; private: template voidShowOperationMessage(boolisNew) { LockRealselock(_threadMutex); consttype_info&nInfo=typeid(T); QStringclassName=nInfo.name(); className=TrimClassName(className); AddCount(className,isNew); if(isNew) { _newCount++; } else { _deleteCount++; } if(!_showDetailMessage) { return; } if(isNew) { qDebug()<<"*New"< ::ConstIteratori=_mapMemObjCount.find(className); if(i==_mapMemObjCount.constEnd()) { MemObjInfo*info=newMemObjInfo(); info->ClassName=className; if(isNew) { info->NewCount++; } else { info->DeletCount++; } _mapMemObjCount.insert(className,info); } else { MemObjInfo*info=i.value(); if(isNew) { info->NewCount++; } else { info->DeletCount++; } } } voidShowNewDeleteDetail(boolisShowAll) { QMap ::ConstIteratori=_mapMemObjCount.cbegin(); for(;i!=_mapMemObjCount.cend();i++) { MemObjInfo*info=i.value(); intleftNew=info->NewCount-info->DeletCount; if(leftNew!=0) { qDebug()<<"***obj"< ClassName<<"New:"< NewCount <<"Delete:"< DeletCount <<"Diff:"< ClassName<<"New:"< NewCount <<"Delete:"< DeletCount <<"Diff:"< _mapMemObjCount; }; #endif//MEMMANAGE_H 后记
解决内存泄漏的方法很多。本文介绍了一种行之有效的方法。开发一个新项目前,就需确定如何跟踪定位内存泄漏,发现问题越早解决起来越简单。程序开发是循序渐进的过程,一个功能模块开发完成后,需及早确定是否有内存泄漏。防微杜渐,步步为营,方能产出高质量的产品。
以上就是c++防止内存泄漏的妙招的详细内容,更多关于c++防止内存泄漏的资料请关注毛票票其它相关文章!
声明:本文内容来源于网络,版权归原作者所有,内容由互联网用户自发贡献自行上传,本网站不拥有所有权,未作人工编辑处理,也不承担相关法律责任。如果您发现有涉嫌版权的内容,欢迎发送邮件至:czq8825#qq.com(发邮件时,请将#更换为@)进行举报,并提供相关证据,一经查实,本站将立刻删除涉嫌侵权内容。