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操作构造函数没有参数
template
T*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(发邮件时,请将#更换为@)进行举报,并提供相关证据,一经查实,本站将立刻删除涉嫌侵权内容。