fangyukuan

永无止境的追求...追求卓越!!!

  博客园 :: 首页 :: 博问 :: 闪存 :: 新随笔 :: 联系 :: 订阅 订阅 :: 管理 ::
  94 随笔 :: 0 文章 :: 125 评论 :: 0 引用

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

 

 

 

 

posted on 2012-05-07 01:24 fangyukuan 阅读(...) 评论(...) 编辑 收藏