警惕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中的堆上分配內存逐個釋放。。。

日~

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM