json筆記-jsoncpp一個全局對象的bug


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

 

 

 

 


免責聲明!

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



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