今天在工程中使用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