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