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