C++之tinyXML的内存泄漏问题

本文讲述了在使用TinyXML解析XML时,需关注内存管理(包括InsertEndChild和LinkEndChild的区别)、异常处理以及字符编码,同时强调了避免内存泄漏的重要性,通过实例展示了如何解决TinyXML中的内存泄漏问题。
摘要由CSDN通过智能技术生成

TinyXML 是一个用于解析 XML 的 C++ 库。使用 TinyXML 进行 XML 处理时需要注意以下几点:

  • 节点类型:TinyXML 中的节点分为元素节点、文本节点、注释节点等。在解析 XML 时,需要根据具体节点类型进行相应的处理。

  • 内存管理:TinyXML 使用内存池来管理内存分配。在使用 TinyXML 时,需要注意避免内存泄漏,及时释放占用的内存。

  • 导出和导入:TinyXML 可以将 XML 文件导出为字符串或者从字符串中导入 XML 数据。需要注意导出和导入数据格式的一致性。

  • 异常处理:在解析 XML 文件时,可能会出现各种异常情况(如文件不存在、文件格式不正确等)。需要加入相应的异常处理机制,使程序具有更好的健壮性。

  • 字符编码:XML 文件中可能包含不同的字符编码,使用 TinyXML 解析时需要确保正确处理字符编码,避免出现乱码等问题。

使用 TinyXML 需要特别注意内存管理、异常处理以及字符编码等问题,并且需要根据实际需求灵活运用 TinyXML 提供的功能。

如果你看了TinyXml的文档或者一些网友写的例子程序, 你会发现, 其中new出了很多对象而不见一个delete, 这个问题我当时也感觉特别难以接受, 网上也有各种各样的说法, 有说"nwe出这么多, 内存不泄漏才怪", 有说"TinyXml的指针有自销毁功能"等等,这个问题困扰了我一周多了,最终终于定位到泄漏的地方了,瞬间通畅了许多,舒服。
说多没有用,还得亲自测试。
大家看一下,一下程序是否会造成内存泄漏

 #include <iostream>
 #include <string>
 #include "tinyxml.h"
  using namespace std;
  int main() {
    string  xmlStr1 = "<?xml version=\"1.0\" encoding=\"gb2312\"?><Notify></Notify>";
	TiXmlDocument *doc;
    doc = new TiXmlDocument();
	doc->Parse(xmlStr1.c_str());

    TiXmlElement* rootElement = doc->RootElement();

    // 生成新节点
    TiXmlElement* pNewNode = new TiXmlElement("key");
    if (!pNewNode) {
        return -1;
    }
    // 设置节点文本,然后插入节点
    TiXmlText* pNewValue = new TiXmlText("value");
    pNewNode->LinkEndChild(pNewValue);
    rootElement->InsertEndChild(*pNewNode);
    doc->Print();
    delete doc;
    }

使用valgrind内存泄漏检测工具测试,以下为输出结果:

[root@cambricon /cambricon/work/run]# cp /mnt/testxml .
[root@cambricon /cambricon/work/run]# ./valgrind --leak-check=full --track-origins=yes ./testxml  \r
==2947== Memcheck, a memory error detector
==2947== Copyright (C) 2002-2022, and GNU GPL'd, by Julian Seward et al.
==2947== Using Valgrind-3.19.0 and LibVEX; rerun with -h for copyright info
==2947== Command: ./testxml r
==2947== 
<?xml version="1.0" encoding="gb2312" ?>
<Notify>
    <key>value</key>
</Notify>
==2947== 
==2947== HEAP SUMMARY:
==2947==     in use at exit: 328 bytes in 2 blocks
==2947==   total heap usage: 10 allocs, 8 frees, 75,017 bytes allocated
==2947== 
==2947== 328 (216 direct, 112 indirect) bytes in 1 blocks are definitely lost in loss record 2 of 2
==2947==    at 0x48449E8: operator new(unsigned long) (vg_replace_malloc.c:422)
==2947==    by 0x40740B: main (testtinyxml.cpp:210)
==2947== 
==2947== LEAK SUMMARY:
==2947==    definitely lost: 216 bytes in 1 blocks
==2947==    indirectly lost: 112 bytes in 1 blocks
==2947==      possibly lost: 0 bytes in 0 blocks
==2947==    still reachable: 0 bytes in 0 blocks
==2947==         suppressed: 0 bytes in 0 blocks
==2947== 
==2947== For lists of detected and suppressed errors, rerun with: -s
==2947== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 0 from 0)

很明显,造成了内存泄漏,指出了在哪里造成了内存泄漏

==2947==    at 0x48449E8: operator new(unsigned long) (vg_replace_malloc.c:422)
==2947==    by 0x40740B: main (testtinyxml.cpp:210)

在这里插入图片描述
好,这里加上delete后,重新执行。
在这里插入图片描述
输出结果:

[root@cambricon /cambricon/work/run]# ./valgrind --leak-check=full --track-origins=yes ./testxml  \r
==2960== Memcheck, a memory error detector
==2960== Copyright (C) 2002-2022, and GNU GPL'd, by Julian Seward et al.
==2960== Using Valgrind-3.19.0 and LibVEX; rerun with -h for copyright info
==2960== Command: ./testxml r
==2960== 
<?xml version="1.0" encoding="gb2312" ?>
<Notify>
    <key>value</key>
</Notify>
==2960== 
==2960== HEAP SUMMARY:
==2960==     in use at exit: 0 bytes in 0 blocks
==2960==   total heap usage: 10 allocs, 10 frees, 75,017 bytes allocated
==2960== 
==2960== All heap blocks were freed -- no leaks are possible
==2960== 
==2960== For lists of detected and suppressed errors, rerun with: -s
==2960== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)

完美,加上之后,就没有内存泄漏了,显示全部被释放了。
在这里插入图片描述
那么这是为什么呢,我们来看一下TiXml源码:

	/** Add a new node related to this. Adds a child past the LastChild.
		Returns a pointer to the new object or NULL if an error occured.
	*/
	TiXmlNode* InsertEndChild( const TiXmlNode& addThis );


	/** Add a new node related to this. Adds a child past the LastChild.

		NOTE: the node to be added is passed by pointer, and will be
		henceforth owned (and deleted) by tinyXml. This method is efficient
		and avoids an extra copy, but should be used with care as it
		uses a different memory model than the other insert functions.

		@sa InsertEndChild
	*/
	TiXmlNode* LinkEndChild( TiXmlNode* addThis );

翻译一下:

/**
 * 在当前节点添加一个新的子节点,将其添加到LastChild之后。
 * 如果发生错误,则返回一个指向新对象的指针;否则返回NULL。
 */
TiXmlNode* InsertEndChild(const TiXmlNode& addThis);


/**
 * 在当前节点添加一个新的子节点,将其添加到LastChild之后。
 *
 * 注意:被添加的节点通过指针传递,并且以后将由TinyXml拥有(并删除)。
 * 这种方法高效且避免了额外的复制,但使用时应谨慎,因为它使用与其他插入函数不同的内存模型。
 *
 * @sa InsertEndChild
 */
TiXmlNode* LinkEndChild(TiXmlNode* addThis);

可以看到InsertEndChildLinkEndChild都可以插入,但是两者的区别是,InsertEndChild是做了一份拷贝,所以后面自己需要对这个申请的内存进行释放,而LinkEndChild为了避免了额外的复制,直接进行地址复制,相当于交给了TiXml去管理这份内存了,自己就不需要手动释放了,手动释放反而会发生错误,造成内存二次释放的问题。
还是看我们的使用习惯把,InsertEndChild后,自己注意管理内存,LinkEndChild则不用管了。
下面是LinkEndChild的方法,也是可以的

#include <iostream>
#include <string>
#include "tinyxml.h"
using namespace std;
int main() {
    string  xmlStr1 = "<?xml version=\"1.0\" encoding=\"gb2312\"?><Notify></Notify>";
    TiXmlDocument *doc;
    doc = new TiXmlDocument();
    doc->Parse(xmlStr1.c_str());

    TiXmlElement* rootElement = doc->RootElement();

    // 生成新节点
    TiXmlElement* pNewNode = new TiXmlElement("key");
    if (!pNewNode) {
        return -1;
    }
    // 设置节点文本,然后插入节点
    TiXmlText* pNewValue = new TiXmlText("value");
    pNewNode->LinkEndChild(pNewValue);
    rootElement->LinkEndChild(pNewNode);   
    doc->Print();

    delete doc;
}

总结:在使用这些第三方库的时候,一定要多注意,很容易造成内存泄漏,最好使用工具监控一下自己的程序,不然内存就偷偷的在泄漏,知道最后炸了。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

CONNY~

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值