linux Qt 内存泄露 写文件
写xml文件时,怀疑有内存泄露,具体哪泄露没有找出来,附一段代码,希望有哪位大神给解决一下:其中XMLSTR 比较大 最大估计得到100多K 再就是Out这个类型 应该是Qtextstream类 能不能被释放掉bool XMLOp::WriteSetTableEx(SetTable *SetTables,QString FilePath,QString Xml){
int i,j,k;
QString *XmlStr;
XmlStr=new QString();
QHash<QString, Settings*>::const_iterator Cite;
QHash<QString,SettingOptions*>::const_iterator Site;
QHash<QString,QString>::const_iterator Oite;
Cite=SetTables->GetIterator();
*XmlStr="<Root>\n";
for(i=0;i<SetTables->Count();i++)
{
Settings *TmpSettings;
QString TmpKey;
TmpKey=Cite.key();
*XmlStr+=" <ClassName>";
*XmlStr +=TmpKey.trimmed()+"\n";
TmpSettings=Cite.value();
Site=TmpSettings->GetIterator();
for(j=0;j<TmpSettings->Count();j++)
{
QString SKey;
SettingOptions *SSettingOptions;
SKey=Site.key();
* XmlStr+=" <ConfigData>";
*XmlStr +=SKey.trimmed()+"\n";
SSettingOptions=Site.value();
Oite=SSettingOptions->GetIterator();
for(k=0;k<SSettingOptions->Count();k++)
{
QString TKey;
QString Unit;
TKey=Oite.key();
*XmlStr+=" <Name>";
*XmlStr+=TKey.trimmed();
*XmlStr+="</Name>\n";
Unit=Oite.value();
*XmlStr+=" <Value>";
* XmlStr+=Unit.trimmed();
*XmlStr+="</Value>\n";
Oite++;
}
*XmlStr+=" </ConfigData>\n";
Site++;
}
*XmlStr+=" </ClassName>\n";
Cite++;
}
*XmlStr+=Xml+"\n";
*XmlStr+="</Root>";
//add file operation here
if(QFile::exists(FilePath))
{
QFile::remove(FilePath);
}
//Log.Log("开始写入配置文件!\r");
QFile *file;
file=new QFile();
file->setFileName(FilePath);
if(!file->open(QIODevice::WriteOnly | QIODevice::Truncate) ) return false ;
QTextStream out(file);
int XmlCount;
XmlCount=0;
out<<*XmlStr;
file->flush();
file->close();
XmlStr->clear();
delete XmlStr;
XmlStr=NULL;
delete file;
return true;
} --------------------编程问答-------------------- 只是一个函数内的QString没必要new吧,(QString *XmlStr; XmlStr=new QString();)
还有QFile也没有必要new吧。直接在栈上面声明变量不是很方便吗,函数结束自动销毁,回收内存。
比如: if(!file->open(QIODevice::WriteOnly | QIODevice::Truncate) ) return false ;
这一行代码,如果失败 return false,前面new 的QString、QFile全部没有释放,内存泄漏了。 --------------------编程问答--------------------
+1 没必要的就不要new和delete了,一般我用C++的代码风格是一个函数只有一个返回点,就是函数的最后1行的return,像你这种多个return的风格就容易出现这种问题了,当然在有垃圾回收机制的语言里,多个返回点也是被推荐的,有时候良好的代码风格可以帮助你避免不少问题。 --------------------编程问答-------------------- 同问~~
1.是不是QT本身对Hash数据结构的访问机制存在问题呢,使用这种ITL迭代的方式进行访问,是否本身就可能是内存泄露?
2.在排除内存泄露的时候,是否需要对QT库怀疑,现在始终感觉QT的某些机制上是存在问题的,比如QTimer等
恭候大神出现哈~!
--------------------编程问答--------------------
能说得具体点,探讨下? --------------------编程问答--------------------
原来在写一个代码的时候,代码如下:
// mMutex.lock();
QHash<QString,DataPoolUnit>::iterator i;
float Sum;
Sum=0;
i=Datas->begin();
// DataPoolUnit Unit;
while(i!=Datas->end())
{
// Unit=i.value();
if(i.value().ParaType==ParaType)
{
Sum+=i.value().ParaValue.toFloat();
}
i++;
}
// mMutex.unlock();
return Sum;
如果不用上面的方式,而是用 DataPoolUnit Unit; 来获取某个值的时候,就会出现泄漏,但是
DataPoolUnit 我是做好了COPY STRUCTURE和析构的 --------------------编程问答--------------------
你上面的代码2种用法都不存在内存泄漏,没有看出有资源分配的地方,我觉得要是有泄漏的现象,最好还是检查DataPoolUnit的实现
--------------------编程问答--------------------
DatapoolUnit的实现应该没问题,这个可以确认的,我把代码也贴出来,
#include "datapoolunit.h"
DataPoolUnit::DataPoolUnit()
{
this->PLCMD200Data=new QByteArray;
this->PLCMM50Data=new QByteArray;
this->PLCSData=new QByteArray;
this->WarnFlag=0;
this->IsWarnEnable=0;
this->FunctionStatusCode="NA";
this->NameCode="NA";
this->DIFlag="2";
this->Flag="CSD";
this->WarnCount=0;
}
DataPoolUnit::DataPoolUnit(const DataPoolUnit &d)
{
*this=d;
}
DataPoolUnit& DataPoolUnit::operator =(const DataPoolUnit &d)
{
if(this==&d)
{
return*this;
}
else
{
if(this->Flag=="CSD")
{
delete this->PLCSData;
delete this->PLCMM50Data;
delete this->PLCMD200Data;
PLCSData=NULL;
PLCMM50Data=NULL;
PLCMD200Data=NULL;
}
this->PLCMD200Data=new QByteArray;
this->PLCMM50Data=new QByteArray;
this->PLCSData=new QByteArray;
this->EquipID=d.EquipID;
this->IsWarnEnable=d.IsWarnEnable;
this->LastDateTime=d.LastDateTime;
this->LastValue=d.LastValue;
this->NameCode=d.NameCode;
this->ParaClass=d.ParaClass;
this->FunctionStatusCode=d.FunctionStatusCode;
this->ParaID=d.ParaID;
this->ParaName=d.ParaName;
this->ParaType=d.ParaType;
this->ParaValue=d.ParaValue;
* this->PLCMD200Data=*d.PLCMD200Data;
*this->PLCMM50Data=*d.PLCMM50Data;
*this->PLCSData=*d.PLCSData;
this->UpdateTime=d.UpdateTime;
this->WarnFlag=d.WarnFlag;
this->DIFlag=d.DIFlag;
this->WarnCount=d.WarnCount;
return* this;
}
}
DataPoolUnit::~DataPoolUnit()
{
delete this->PLCSData;
delete this->PLCMM50Data;
delete this->PLCMD200Data;
PLCSData=NULL;
PLCMM50Data=NULL;
PLCMD200Data=NULL;
} --------------------编程问答--------------------
你的实现是有问题的,c++中要记住copy constructor不要调用assign operator, 如果他们有可以复用的代码,请剥离一个函数来实现复用。
copy constructor对同一对象只会被调用一次,被调用的时候对象还没有创建;而assign operator是在对象已经存在的情况下调用的,可以多次调用.
你的赋值操作赋的else分支里面,delete一个没有创建的对象的成员,结果应该是未定义的。
另外,建议在构造函数中,优先使用初始化列表,而不是在函数内进行赋值 --------------------编程问答-------------------- 可以参考:
http://stackoverflow.com/questions/2639017/calling-assignment-operator-in-copy-constructor
http://stackoverflow.com/questions/2639017/calling-assignment-operator-in-copy-constructor --------------------编程问答-------------------- http://stackoverflow.com/questions/1533725/is-it-bad-form-to-call-the-default-assignment-operator-from-the-copy-constructor --------------------编程问答--------------------
对于您说的意思,我的理解如下:
1.对于copy构造来说,被copy的对象本身是存在的,而copy出的对象是被创建的,所以不能用等号?但是很多时候,都是用等号来进行copy构造的?
2.如果不用等号,应该用什么方式来进行呢,一个单独的函数吗?
3.那个else分支里的,有个Flag,那个Flag就是用来标识1这种情况的,是否被构造过;
如有不对之处,还请指正~! --------------------编程问答--------------------
另外,能不能写个具体示例学习下,还有请看看1楼的代码有问题吗? --------------------编程问答--------------------
对于1和2:
A a;
A a1 = a; // Copy ctor, not assignment operator
A a2;
a2 = a; // Assignment operator
对于3:
这个flag是不需要的,你在一个已经构建了得对象内部判断这个对象有没有被构造,这本身就是个问题。你正确的实现方式应该是,定义一个init函数,将copy ctor和 assignment operator重复的代码放在里面,然后被这两者调用。
--------------------编程问答--------------------
本人新手,尚请见谅,还得继续麻烦您,主要是还是有点不理解,呵呵,还请指教,这个代码:
A a;
A a1 = a; // Copy ctor, not assignment operator
您的意思就是说,虽然是=,但是实际上调用的是copy constructor,而不是重载的=操作符?
针对于3,这个对象本身被构建了,但是不是仍然可能进行等号赋值操作呢,如果进行了等号赋值,那么这个不属于copy构造的范畴,那么应该只是重载等号,进行操作,是这样理解吗?如果本身被构建了,那么进行重载赋值的时候,是不是考虑要释放资源呢,不然是否还是会内存泄露~
--------------------编程问答-------------------- @freebendy
方便的话,是否可以加一下qq~ --------------------编程问答--------------------
=只是一个符号,实际上调用copy ctor还是assignment operator是由编译器决定,我们的代码比较保证在这些情况下都没有问题,不能设置一些假想,对于3,如果你不让copy ctor调用assignment operator,这个flag就是不需要的,因为在赋值的时候你必须释放已分配的资源。 --------------------编程问答--------------------
私信你了,上班时间qq和mail都上不了的,只能上网 --------------------编程问答--------------------
1.ok,已经加了您的qq
2.那么这样我的copy构造就需要重新写了,实际上对于=这种做法我也发现了对于已经构建的对象是有问题的,所以才会加标志位,这样一说 我就更清楚了,这样我改下试试看
3.还请您麻烦看看下面的代码,为什么会造成泄漏
//曲线数据存储,一天一个文件,300个节点
//1.判断文件是否存在
//2.如果文件不存在,则新建,并将XML字符串直接写入文件,形成SETTABLE类可识别的格式
//3.如果文件存在,则利用SETTABLE将其读入内存,然后利用方法将其生成XML字符串,将本次需要
//追加的内容加载到字符串后,然后重新写入XML文件,释放临时资源
void SaveHisData::DataGraphicSaveEx(QString mCurtTime,int GraphicNo)
{
//首先判断 xml文件是否存在
//根据当前日期,生成文件名
QDateTime CTime;
QString DirName;
QString FileName;
CTime=QDateTime::fromString(mCurtTime,"yyyy-MM-dd HH:mm:ss");
DirName=CTime.toString("yyyyMMdd");
DirName="/media/mmcblk0p1/Graphic/"+DirName; //在相应的文件夹下建立 文件夹
QDir temp ; //判断文件夹是否存在
if (!(temp.exists(DirName)))
{
temp.mkdir(DirName); //创建文件夹
}
//--------------------创建文件
FileName="Graphic"; //文件的名称为 G1 G2 ... G300
QString FilePath;
FilePath=DirName+"/"+FileName+".xml";
QFile File;
// File=new QFile();
if (!File.exists(FilePath)) //如果文件不存在
{
//如果不存在,就先创建一个文件
File.setFileName(FilePath);
//对文件的操作----------写数据
if (File.open(QIODevice::WriteOnly | QIODevice::Append))
{
QTextStream out(&File);
QString XmlStr;
XmlStr+="<Root>\n";
XmlStr+=" <ClassName>";
XmlStr+= QString::number(GraphicNo)+"\n"; //格式: 1
//压力的数据 //参数ID固定为 ExtPLCSumPa
QString YLSum;
YLSum=QString::number(this->mDataPool->GetSumValueByParaType("压力"),'f',2);
XmlStr+=" <ConfigData>";
XmlStr+="ExtPLCSumPa\n";
XmlStr+=" <Name>设备ID</Name>\n";
XmlStr+=" <Value></Value>\n";
XmlStr+=" <Name>参数ID</Name>\n";
XmlStr+=" <Value>ExtPLCSumPa</Value>\n";
XmlStr+=" <Name>参数值</Name>\n";
XmlStr+=" <Value>"+YLSum.trimmed()+"</Value>\n";
XmlStr+=" <Name>更新时间</Name>\n";
XmlStr+=" <Value>"+CTime.toString("yyyy-MM-dd HH:mm:ss")+"</Value>\n";
XmlStr+=" <Name>参数名称</Name>\n";
XmlStr+=" <Value>总压力</Value>\n";
XmlStr+=" <Name>参数类型</Name>\n";
XmlStr+=" <Value>总压力</Value>\n";
XmlStr+=" </ConfigData>\n";
//获取总瞬时流量 //参数ID固定为 FlowSumE
QString SSLLSum;
SSLLSum=QString::number(this->mDataPool->GetSumValueByParaType("瞬时流量"),'f',2);
XmlStr+=" <ConfigData>";
XmlStr+="FlowSumE\n";
XmlStr+=" <Name>设备ID</Name>\n";
XmlStr+=" <Value></Value>\n";
XmlStr+=" <Name>参数ID</Name>\n";
XmlStr+=" <Value>FlowSumE</Value>\n";
XmlStr+=" <Name>参数值</Name>\n";
XmlStr+=" <Value>"+SSLLSum.trimmed()+"</Value>\n";
XmlStr+=" <Name>更新时间</Name>\n";
XmlStr+=" <Value>"+CTime.toString("yyyy-MM-dd HH:mm:ss")+"</Value>\n";
XmlStr+=" <Name>参数名称</Name>\n";
XmlStr+=" <Value>总瞬时流量</Value>\n";
XmlStr+=" <Name>参数类型</Name>\n";
XmlStr+=" <Value>总瞬时流量</Value>\n";
XmlStr+=" </ConfigData>\n";
XmlStr+=" </ClassName>\n";
XmlStr+="</Root>\n";
out<<XmlStr;
XmlStr.clear();
}
File.flush();
File.close();
}
else//如果文件存在
{
SetTable Tmp;
this->Xop->CreateSetTableByXml(FilePath,&Tmp);
QString XmlStr;
XmlStr+=" <ClassName>";
XmlStr+= QString::number(GraphicNo)+"\n"; //格式: 1
//压力的数据 //参数ID固定为 ExtPLCSumPa
QString YLSum;
YLSum=QString::number(this->mDataPool->GetSumValueByParaType("压力"),'f',2);
XmlStr+=" <ConfigData>";
XmlStr+="ExtPLCSumPa\n";
XmlStr+=" <Name>设备ID</Name>\n";
XmlStr+=" <Value></Value>\n";
XmlStr+=" <Name>参数ID</Name>\n";
XmlStr+=" <Value>ExtPLCSumPa</Value>\n";
XmlStr+=" <Name>参数值</Name>\n";
XmlStr+=" <Value>"+YLSum.trimmed()+"</Value>\n";
XmlStr+=" <Name>更新时间</Name>\n";
XmlStr+=" <Value>"+CTime.toString("yyyy-MM-dd HH:mm:ss")+"</Value>\n";
XmlStr+=" <Name>参数名称</Name>\n";
XmlStr+=" <Value>总压力</Value>\n";
XmlStr+=" <Name>参数类型</Name>\n";
XmlStr+=" <Value>总压力</Value>\n";
XmlStr+=" </ConfigData>\n";
//获取总瞬时流量 //参数ID固定为 FlowSumE
QString SSLLSum;
SSLLSum=QString::number(this->mDataPool->GetSumValueByParaType("瞬时流量"),'f',2);
XmlStr+=" <ConfigData>";
XmlStr+="FlowSumE\n";
XmlStr+=" <Name>设备ID</Name>\n";
XmlStr+=" <Value></Value>\n";
XmlStr+=" <Name>参数ID</Name>\n";
XmlStr+=" <Value>FlowSumE</Value>\n";
XmlStr+=" <Name>参数值</Name>\n";
XmlStr+=" <Value>"+SSLLSum.trimmed()+"</Value>\n";
XmlStr+=" <Name>更新时间</Name>\n";
XmlStr+=" <Value>"+CTime.toString("yyyy-MM-dd HH:mm:ss")+"</Value>\n";
XmlStr+=" <Name>参数名称</Name>\n";
XmlStr+=" <Value>总瞬时流量</Value>\n";
XmlStr+=" <Name>参数类型</Name>\n";
XmlStr+=" <Value>总瞬时流量</Value>\n";
XmlStr+=" </ConfigData>\n";
XmlStr+=" </ClassName>\n";
this->Xop->WriteSetTableEx(&Tmp,FilePath,XmlStr);
Tmp.ReleaseResource();
}
} --------------------编程问答-------------------- 另,改写了DatapoolUnit的实现,请您看下,十分感谢!!!!
#include "datapoolunit.h"
DataPoolUnit::DataPoolUnit()
{
this->PLCMD200Data=new QByteArray;
this->PLCMM50Data=new QByteArray;
this->PLCSData=new QByteArray;
this->WarnFlag=0;
this->IsWarnEnable=0;
this->FunctionStatusCode="NA";
this->NameCode="NA";
this->DIFlag="2";
this->Flag="CSD";
this->WarnCount=0;
}
DataPoolUnit::DataPoolUnit(const DataPoolUnit &d)
{
this->PLCMD200Data=new QByteArray;
this->PLCMM50Data=new QByteArray;
this->PLCSData=new QByteArray;
this->EquipID=d.EquipID;
this->IsWarnEnable=d.IsWarnEnable;
this->LastDateTime=d.LastDateTime;
this->LastValue=d.LastValue;
this->NameCode=d.NameCode;
this->ParaClass=d.ParaClass;
this->FunctionStatusCode=d.FunctionStatusCode;
this->ParaID=d.ParaID;
this->ParaName=d.ParaName;
this->ParaType=d.ParaType;
this->ParaValue=d.ParaValue;
* this->PLCMD200Data=*d.PLCMD200Data;
*this->PLCMM50Data=*d.PLCMM50Data;
*this->PLCSData=*d.PLCSData;
this->UpdateTime=d.UpdateTime;
this->WarnFlag=d.WarnFlag;
this->DIFlag=d.DIFlag;
this->WarnCount=d.WarnCount;
}
DataPoolUnit& DataPoolUnit::operator =(const DataPoolUnit &d)
{
if(this==&d)
{
return*this;
}
else
{
this->EquipID=d.EquipID;
this->IsWarnEnable=d.IsWarnEnable;
this->LastDateTime=d.LastDateTime;
this->LastValue=d.LastValue;
this->NameCode=d.NameCode;
this->ParaClass=d.ParaClass;
this->FunctionStatusCode=d.FunctionStatusCode;
this->ParaID=d.ParaID;
this->ParaName=d.ParaName;
this->ParaType=d.ParaType;
this->ParaValue=d.ParaValue;
* this->PLCMD200Data=*d.PLCMD200Data;
*this->PLCMM50Data=*d.PLCMM50Data;
*this->PLCSData=*d.PLCSData;
this->UpdateTime=d.UpdateTime;
this->WarnFlag=d.WarnFlag;
this->DIFlag=d.DIFlag;
this->WarnCount=d.WarnCount;
return *this;
}
}
DataPoolUnit::~DataPoolUnit()
{
delete this->PLCSData;
delete this->PLCMM50Data;
delete this->PLCMD200Data;
PLCSData=NULL;
PLCMM50Data=NULL;
PLCMD200Data=NULL;
}
--------------------编程问答--------------------
你这函数也太长了吧?memory leak没注意看出来,其他问题倒不少:
1.代码c风格, 遵循RAII,变量的声明和初始化写到一起吧,
2. if (!File.exists(FilePath)) //如果文件不存在 ---QFile有这个代参数的方法?
3. 组装字符串就抽离出去吧,跟程序逻辑混合在一起,降低代码可读性。
养成良好的编码风格很重要,可以避免很多的低级错误。 --------------------编程问答-------------------- 另外,别再在别人的地盘继续下去了 --------------------编程问答--------------------
恩,代码是比较长,主要是XML字符串组装的过程,其实逻辑很简单,Qfile是有这个方法的 --------------------编程问答--------------------
热烈欢迎在此讨论问题,大神就是大神。。。。解惑啊!!!! --------------------编程问答--------------------
没事的,我想楼主也是欢迎我们讨论的,其实楼主就是我同事,哈哈 --------------------编程问答-------------------- 大神还在吗,还等着解惑呢....真心求教啊...... --------------------编程问答--------------------
问题还是有啊
1.DataPoolUnit::DataPoolUnit() 所有成员的初始化都可以放到初始化列表的,这样可以提高效率。
2.同理DataPoolUnit::DataPoolUnit(const DataPoolUnit &d)也可以这样做;此外你的那些QByteArray可以使用它的copy ctor啊,为什么先创建再赋值,很低效。
3.在析构函数,重新赋值为NULL其实是没有必要的,因为对象被析构了,这些指针你也用不了 --------------------编程问答--------------------
恩,您说的问题,我会再改动下,现在,
1.这样写是不是就是没有问题的呢,不会存在泄漏什么的吧,原理理解上应该没有误差了吧?
2.再就是您说的初始化列表,是不是就是指一个Init函数呢? --------------------编程问答--------------------
初始化列表是:
A::A(int a, int b): mA(a), mB(b), mC(NULL)
{
}
--------------------编程问答--------------------
好的,学习了.... --------------------编程问答-------------------- 早上来了,继续来盖楼~哈哈
现在还有个问题,就是两个窗体互相调用的问题,比如需要在窗体A中调用显示窗体B,同时关闭窗体A,应该用什么样的方式比较好呢? --------------------编程问答--------------------
所谓的关闭是什么意思?如果只是表面上关闭,可以把窗口隐藏了,也可以由一个父窗口控制AB2个子窗口,主要看你的应用场景考虑 --------------------编程问答--------------------
关闭是真正意义的关闭,而不是隐藏~,主要是考虑在嵌入式板子上运行,内存空间有限,那个属性Qt::WA_DeleteOnClose已经设置,想知道如何进行窗体互调,看看自己的写法有没有错误 --------------------编程问答-------------------- 应该没问题吧,你可以测试下看看窗口被销毁了没 --------------------编程问答--------------------
测试了,窗口确实被销毁了,但是内存没有降低~ --------------------编程问答--------------------
你一个窗口占用内存才多大啊,你要测试就要用占用内存很大的窗口对象的看看内存有没有降低 --------------------编程问答--------------------
就是这么做的,放了好多控件。。。。。 --------------------编程问答--------------------
大神,看看这个问题怎么解决啊?麻烦了,多谢!!
http://bbs.csdn.net/topics/390357552?page=1
补充:移动开发 , Qt