热门标签 | HotTags
当前位置:  开发笔记 > 运维 > 正文

c++排查内存泄漏的妙招

这篇文章主要介绍了c++如何用辅助类排查内存泄漏,帮助大家更好的理解和学习使用c++,感兴趣的朋友可以了解下

前言

对于c++而言,如何查找内存泄漏是程序员亘古不变的话题;解决之道可谓花样繁多。因为最近要用到QT写程序,摆在我面前的第一个重要问题是内存防泄漏。如果能找到一个简单而行之有效的方法,对后续开发大有裨益。久思终得诀窍,本文就详细介绍我对此问题的应对之策。(文末符完整代码)

如何判断内存有泄漏

  内存分配和释放对应的操作是new、delete。如何判断内存是否释放干净?其实判断起来非常简单:一个独立的模块整个生存周期内new的个数和delete的个数相等。用伪代码标示如下:

int newCount = 0;
 int deleteCount = 0;

 //new 操作时
 new class();
 newCount++;

 //delete 操作时
 delete* objPtr;
 deleteCount++;

 //模块结束时
 if(newCount != deleteCount)
 {
 内存有泄漏
 }

如果对所有的new和delete操作,加上如上几行代码,就能发现是否有内存泄漏问题。如果采用上面方法解决问题,手段太low了。

我们的方法有如下特点:

1 使用起来超级简单,不增加开发难度。

2 发生内存泄漏时,能定位到具体是哪个类。

托管new delete 操作符

  要跟踪所有的new、delete操作,最简单的办法就是托管new、delete。不直接调用系统的操作符,而是用我们自己写的函数处理。在我们的函数内部,则别有洞天; 对new和delete的跟踪和记录就为我所欲也。托管new和delete需用到模板函数,代码如下:

class MemManage
{
 //单实例模式
private:
 static MemManage* _instance_ptr;
public:
 static MemManage* instance()
 {
 if (_instance_ptr == nullptr)
 {
  _instance_ptr = new MemManage();
 }
 return _instance_ptr;
 }

public:
 MemManage();

 //new操作 构造函数没有参数
 template 
 T* New()
 {
 ShowOperationMessage(true);
 return new T();
 };

 //new操作 构造函数有1个参数
 template 
 T* New(TParam1 param)
 {
 ShowOperationMessage(true);
 return new T(param);
 };

 //new操作 构造函数有2个参数
 template 
 T* New(TParam1 param1, TParam2 param2)
 {
 ShowOperationMessage(true);
 return new T(param1, param2);
 };

 //delete 操作
 template 
 void Delete(T t)
 {
 if (t == nullptr)
  return;

 ShowOperationMessage(false);
 delete t;
 };

 //记录new delete
 template 
 void ShowOperationMessage(bool isNew)
 {
 //操作符对应的类名称
 const type_info& nInfo = typeid(T);
 QString className = 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);

 一个模块调用周期结束 调用下列代码,查看是否有内存泄漏:

void ShowNewDelete(bool isShowDetail)
 {
 int leftNew = _newCount - _deleteCount;
 qDebug() <<"***********************";
 qDebug() <<"total New:" <<_newCount <<" Delete:" <<_deleteCount <<" leftNew:" <ShowNewDelete(true);
 //debug输出如下,如果leftNew为0,则没内存泄漏
 total New : 166 Delete : 6 leftNew : 160

进一步定位内存泄漏问题

  通过判断new和delete的个数是否相等,只是知道了是否有内存泄漏;进一步定位问题,才能方便我们解决问题。如果能定位到操作哪一个类时,发生了内存泄漏,则问题范围就大大缩小。我们可以按类名,记录new和delete操作个数,c++获取类名函数如下:

const type_info &nInfo = typeid(T);
QString className = nInfo.name();

建立一个map表,记录类名对应的操作信息:

//每个类 统计的信息
class MemObjInfo
{
public:
 int NewCount = 0;
 int DeletCount = 0;
 QString ClassName;
};

//map对照表
QMap _mapMemObjCount;

//按类名统计
void AddCount(QString& className, bool isNew)
{
 QMap::ConstIterator i = _mapMemObjCount.find(className);
 if (i == _mapMemObjCount.constEnd())
 {
 MemObjInfo* info = new MemObjInfo();
 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发生内存泄漏,就很容易定位问题了。

辅助类完整代码:

#ifndef MEMMANAGE_H
#define MEMMANAGE_H

#include 
#include 
#include 

class LockRealse
{
public:
 LockRealse(QMutex* mutex)
 {
 _mutex = mutex;
 _mutex->lock();
 }

 ~LockRealse()
 {
 _mutex->unlock();
 }

private:
 QMutex* _mutex;
};

class MemObjInfo
{
public:
 int NewCount = 0;
 int DeletCount = 0;
 QString ClassName;
};


class MemManage
{
private:
 static MemManage* _instance_ptr;
public:
 static MemManage* instance()
 {
 if(_instance_ptr==nullptr)
 {
  _instance_ptr = new MemManage();
 }
 return _instance_ptr;
 }

public:
 MemManage()
 {
 _threadMutex = new QMutex();
 _newCount = 0;
 _deleteCount = 0;
 }

 template 
 T* New()
 {
 ShowOperationMessage(true);
 return new T();
 };

 template 
 T* New(TParam1 param)
 {
 ShowOperationMessage(true);
 return new T(param);
 };

 template 
 T* New(TParam1 param1,TParam2 param2)
 {
 ShowOperationMessage(true);
 return new T(param1,param2);
 };

 template 
 void Delete(T t)
 {
 if(t == nullptr)
  return;

 ShowOperationMessage(false);
 delete t;
 };

 void ShowNewDelete(bool isShowDetail)
 {
 int leftNew = _newCount-_deleteCount;
 qDebug()<<"***********************";
 qDebug()<<"total New:"<<_newCount<<" Delete:"<<_deleteCount<<" leftNew:"<
 void clearAndDelete(QList& list)
 {
 foreach(T item ,list)
 {
  // Delete(item);
 }

 list.clear();
 };

private:
 template 
 void ShowOperationMessage(bool isNew)
 {
 LockRealse lock(_threadMutex);
 const type_info &nInfo = typeid(T);
 QString className = nInfo.name();
 className=TrimClassName(className);
 AddCount(className,isNew);

 if(isNew)
 {
  _newCount++;
 }
 else
 {
  _deleteCount++;
 }

 if(!_showDetailMessage)
 {
  return ;
 }

 if(isNew)
 {
  qDebug()<<"*New"<::ConstIterator i = _mapMemObjCount.find(className);
 if(i == _mapMemObjCount.constEnd())
 {
  MemObjInfo* info = new MemObjInfo();
  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++;
  }
 }
 }

 void ShowNewDeleteDetail(bool isShowAll)
 {
 QMap::ConstIterator i = _mapMemObjCount.cbegin();
 for(;i!=_mapMemObjCount.cend();i++)
 {
  MemObjInfo *info = i.value();
  int leftNew =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++ 防止内存泄漏的资料请关注其它相关文章!


推荐阅读
author-avatar
三生石512606
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有