• 警惕rapidxml的陷阱:添加节点时,请保证变量的生命周期


    http://www.cnblogs.com/chutianyao/p/3246592.html

    项目中要使用xml打包、解析协议,HQ指定了使用rapidxml--号称是最快的xml解析器。

    功能很快完成了,但发现rapidxml为了追求性能,做了一些对用户来说并不友好的设计。下面来说一说:

    给xml对象在添加节点时,不可添加临时变量

    按照一般用法,使用如下方式添加节点:

    复制代码
    rapidxml::xml_document<> doc;
    
    void addNode(std::string value)
    {
       rapidxml::xml_node<>* root = doc.allocate_node(rapidxml::node_element, "unregister_context");
       doc.append_node(root);
    
       root->append_node(doc.allocate_node(rapidxml::node_element, "who_register", value.c_str()));
    }
    复制代码

     但在rapidxml中这么写实有问题的,得这么写:

    复制代码
    rapidxml::xml_document<> doc;
    
    void addNode(std::string value)
    {
       rapidxml::xml_node<>* root = doc.allocate_node(rapidxml::node_element, "unregister_context");
       doc.append_node(root);
    
       root->append_node(doc.allocate_node(rapidxml::node_element, "who_register", doc.allocate_string(value.c_str())));
    }
    复制代码

    看出差别了吗?

    待插入的值"变量value"是作为参数传递进来的,是临时变量。rapidxml为了追求极致性能,在append_node()函数中是直接通过指针来访问value变量的,并没有进行内存拷贝--因此rapidxml在这里提出了一个隐晦的前提条件:在xml对象doc的生命周期内,必须保证"变量value"能够被正常访问。

    那么实际情况呢?

    仔细检查一下,就会发现"变量value"是临时变量,在addNode()函数执行完毕后就会被销毁;此时xml对象rapidxml::xml_document<> doc内部保存的值还指向“变量value”的内存地址,而该地址已经不可用了。因此在访问xml对象时就会发生segment fault。

    问题出现了,该怎么解决?我们是无法控制临时变量的生命周期的,因此只能对该变量进行拷贝。rapidxml已经提供了该功能,这就是allocate_string()函数。该函数在rapidxml对象内部的内存池中为我们的变量申请了一份内存,然后将“变量value”的值拷贝过去;由于是xml对象自己维护该内存池,因此就不存在变量地址失效的问题了。

    以上情况仅针对allocate_node()待插入的值是临时变量这种情况;如果用户能保证待插入变量的生命周期、或者是常量,应该不需要使用allocate_string()函数来分配内存了。例如:

    rapidxml::xml_node<>* root = doc.allocate_node(rapidxml::node_element, "data_coming", "some data");

    这里第三个参数"some data"是常量,生命周期等于整个程序的生命周期,因此就不用再为它分配内存了。

    (ps:此种情况仅是推测,未做测试。)

    在为xml对象添加节点时,请保证变量的生命周期!

    总结:

    rapidxml为了追求性能,减少内存拷贝,就尽可能的通过指针(内存地址)来访问用户的变量;这就对用户提出了要求:必须保证变量的生存周期,如果变量被销毁了,rapidxml就会访问无效的内存地址,引发不可控的后果。

    而对于普通用户来说,一般都比较少注意到这个细节。

    为了追求性能,而牺牲了一定的可用性。这种设计是否合理?

    PS:刚遇到了类似的问题,解决用了个笨办法。。。

    std::vector<char*> vec;

    ...

    ...

    char * name = new char[128];

    vec.push_back(name);

    ...

    最后xml的doc保存后将vec中的堆上分配内存逐个释放。。。

    日~

  • 相关阅读:
    页面滚屏截图工具推荐
    java总结第二次(剩余内容)//类和对象1
    happy birthday to tbdd tomorrow
    数组增删改查及冒泡
    三个循环方面程序
    三个入门小程序
    java总结第二次//数组及面向对象
    Java总结第一次//有些图片未显示,文章包含基础java语言及各种语句
    后台验证url是不是有效的链接
    img 鼠标滑上后图片放大,滑下后图片复原
  • 原文地址:https://www.cnblogs.com/kex1n/p/3285900.html
Copyright © 2020-2023  润新知