今天在工程中使用jsoncpp時,發現一個問題。
在一個全局對象的析構函數給一個Json::Value賦值的時候,崩潰。現在把問題用一個demo重現出來。如下:
// ------------------------------------------------------------------------- // 文件名 : main.cpp // 創建者 : 方煜寬 // 創建時間 : 2012-5-6 22:50 // 功能描述 : jsoncpp一個全局對象的bug // // ------------------------------------------------------------------------- #include "json.h" class CA { public: CA(); virtual ~CA(); protected: private: }; CA::CA() { } CA::~CA() { Json::FastWriter writer; Json::Value item; item[0u] = 0; item[1u] = "kuan"; // 到這行時崩潰 } CA a; void main() { }
提示:
R6025
-pure virtual function call
我們在崩潰的地方設置一個斷點跟進去。
發現在json_value.cpp調用下面語句時崩潰。
{ value_.string_ = valueAllocator()->duplicateStringValue( value ); }
查看 valueAllocator() ,在src\lib_json\json_value.cpp里,他是一個函數,如下:
static ValueAllocator *&valueAllocator() { static DefaultValueAllocator defaultAllocator; static ValueAllocator *valueAllocator = &defaultAllocator; return valueAllocator; }
它是取得一個靜態變量的針指。
調試發現在使用valueAllocator()時,即DefaultValueAllocator 對象指針時。DefaultValueAllocator 對象已經被析構了。
因為c++中不同的cpp文件中,全局對象和靜態對象 構造和析構 順序是不確定的。
son_value.cpp再看往下看。可以看到。
static struct DummyValueAllocatorInitializer
{ DummyValueAllocatorInitializer() { valueAllocator(); // ensure valueAllocator() statics are initialized before main(). } } dummyValueAllocatorInitializer;
它的作用是確保valueAllocator()在main()函數前被調用。(注意:先構造的后析構,后構造的先析構)
但其實這樣還不能確保在比其它全局對象的構造函數先調用,比其它全局對象晚析構。問題就出在這里了。
解決方案1:
不在全局對象析構函數中使用jsoncpp字符串。就沒問題了。
但有時候會在全局對象析構函數保存一些數據,把它轉成json格式后再存盤。所以這個解決方案,治標不治本。
解決方案2:
提前對 DefaultValueAllocator 類對象進行構造,比其它【全部對象】或【靜態對象】更前構造,這樣DefaultValueAllocator也會比他們更晚析構。
可以在 DummyValueAllocatorInitializer 前面加上一個編譯指令 #pragma init_seg(lib) 如下:
#pragma init_seg(lib) // add by fangyukuan 2012.5.6 static struct DummyValueAllocatorInitializer
{ DummyValueAllocatorInitializer() { valueAllocator(); // ensure valueAllocator() statics are initialized before main(). } } dummyValueAllocatorInitializer;
這個方案,不好的地方就是修改了第三方庫。一般我們是不會去修改第三方庫的。
你有更好方案嗎?有,請告訴我。
其它:
我們再來看看 DefaultValueAllocator 類,都做了些什么?代碼如下:
class DefaultValueAllocator : public ValueAllocator { public: virtual ~DefaultValueAllocator() { } virtual char *makeMemberName( const char *memberName ) { return duplicateStringValue( memberName ); } virtual void releaseMemberName( char *memberName ) { releaseStringValue( memberName ); } virtual char *duplicateStringValue( const char *value, unsigned int length = unknown ) { //@todo invesgate this old optimization //if ( !value || value[0] == 0 ) // return 0; if ( length == unknown ) length = (unsigned int)strlen(value); char *newString = static_cast<char *>( malloc( length + 1 ) ); memcpy( newString, value, length ); newString[length] = 0; return newString; } virtual void releaseStringValue( char *value ) { if ( value ) free( value ); } };
只有一個功能,malloc 一塊內存,把一個指針的數據,拷貝到新內存里。
我個人感覺,這完全沒必要搞一個類成員函數去做這個事。直接寫個功能函數不就完了嘛。也不會有今天這個bug了。簡潔才是美啊。
本文地址:http://www.cnblogs.com/fangyukuan/archive/2012/05/07/2486803.html