万星星@豌豆荚 欢迎加入我们
一个吃软饭的男人!!!!!我只想写程序####
微博:http://weibo.com/wanlianwen
posts - 172,  comments - 1253,  trackbacks - 0

昨天做一个dll,代码很快写完了,然而使用得时候总是遇到string内部指针删除错误,郁闷了一天,今天没去公司,好好研究了一下。
首先看下下面这段代码,声明两个string对象:

std:: string   s1  =   " wlwlxj " ;
std::
string   s2  =   " lxjwlwww " ;

调试状态下可以看到内部指针:
s1=0x00364ff9
s2=0x00365061
然后执行

s2  =  s1;

按下f11,进入xstring源文件:

_Myt &   operator = ( const  _Myt &  _X)         // 赋值操作符
  
{ return  (assign(_X)); }                     // 调用assign函数

继续进入assign(_X)函数:

_Myt &  assign( const  _Myt &  _X)
        
{ return  (assign(_X,  0 , npos)); }   // 调用assign函数
继续进入assign函数,好戏都在这里面:
_Myt& assign(const _Myt& _X, size_type _P, size_type _M)
        
{if (_X.size() < _P)
            _Xran();
        size_type _N 
= _X.size() - _P;
        
if (_M < _N)
            _N 
= _M;
        
if (this == &_X)
            erase((size_type)(_P 
+ _N)), erase(0, _P);
        
else if (0 < _N && _N == _X.size()                        // 这个分支意思就是如果拷贝源有内容且就是就是源本身,并且
            
&& _Refcnt(_X.c_str()) < _FROZEN - 1          // 源字符串引用次数少于255-1次(可见引用次数最多255次),
            
&& allocator == _X.allocator)                           //且源字符和目的字符分配器一致
            
{_Tidy(true);                                             // 删除本身
            _Ptr 
= (_E *)_X.c_str();                                    // 复制内容到目的串
            _Len 
= _X.size();
            _Res 
= _X.capacity();
            
++_Refcnt(_Ptr); }                                             // 增加一次引用

        
else if (_Grow(_N, true))
            
{_Tr::copy(_Ptr, &_X.c_str()[_P], _N);
            _Eos(_N); }

        
return (*this); }

这样结果就是调用=号以后,s2地址和s1地址一样,都是0x00364ff9。

假如我们动态库有这样一个类class DLL接口:

SetString(std::string str)
{
m_str 
= str;
}

在客户调用时候:

std::string str = "wlwlxj";
DLL d;
d.SetString(str); 
// 此时没有深拷贝,而是引用了str内部指针地址
在调用结束的时候,dll内部删除成员变量的时候,会判断m_str内部指针合法性,由于实际分配是在调用端,在dll内部自然检查指针非法。

解决方法就是避免std::string引用计数,接口处修改为SetString(const char*),这样在dll内部分配内存,内部释放,就不会有问题。
posted on 2006-04-18 16:23 万连文 阅读(6798) 评论(9)  编辑 收藏 引用 所属分类: 模板

FeedBack:
# re: std::string一个极其隐晦得问题
2006-04-18 16:55 | cf
此是老问题了,即跨module(exe、dll)间申请/释放内存违例的问题,对发生在传递c++对象并使用时,不仅仅发生在std::string上

原因是由于程序中使用的内存管理多来源于crt提供的例程,而非直接使用操作系统的接口,这些例程都需要维护一些module全局数据(例如维护池、维护空闲块、或者标记已申请的块等等,不同的实现中有不同的作用),当他们被静态连编时,实际上这些“全局数据”就不“全局”了,不同的module各自为政,每份module都有自己的“全局数据”,自身的内存信息不为他人所知,module A的合法内存快自然不可能通得过module B的合法性验证

解决问题的方法有:
1、不要跨module传递c++对象,或者避免释放跨module申请的内存

2、将参与合作的module统统以multithreaded dll方式链入crt库,让他们的“全局”数据真正全局,注意,所有有交互的module都需要动态链入crt,

不推荐第二种方式
  回复  更多评论
  
# re: std::string一个极其隐晦得问题
2006-04-18 20:29 | christanxw
Dll的出口函数最好是用标准的C类型。  回复  更多评论
  
# re: std::string一个极其隐晦得问题
2006-04-18 21:39 | 万连文
最好是用标准的C类型却是是一种准则。
个人认为:作为输出参数可以通过指针避免,输入参数一般没有问题,上面那个string仅仅由于实现上造成的,其实还可以这样避免:
SetString(std::string str)
{
m_str = str.c_str();
}
  回复  更多评论
  
# re: std::string一个极其隐晦得问题
2006-04-20 11:52 | cocalele
SetString(std::string str) 虽然避免了问题,但对象的复制造成了效率下降。我喜欢

<REF>
2、将参与合作的module统统以multithreaded dll方式链入crt库,让他们的“全局”数据真正全局,注意,所有有交互的module都需要动态链入crt,
</REF>
而且为了避免这种内存问题,我还自己做了一个内存回收的实现,所有模块只分配内存就行了  回复  更多评论
  
# re: std::string一个极其隐晦得问题
2006-04-20 19:53 | Squirrel
那么,使用这样不是更好?
SetString( const std::string & str ){
m_str = str;
}  回复  更多评论
  
# re: std::string一个极其隐晦得问题
2006-04-20 22:17 | 万连文
SetString( const std::string & str ){
m_str = str;
}
这样不可以,因为这样m_str引用str地址,假如导出类对象是成员变量m_expOBJ,
有这样一段代码:
str = "wlw";
m_expOBJ.SetString(str);
m_expOBJ的m_str引用str指针,作用域过去后str析构,此时由于指针被引用,没有delete内存,而m_expOBJ析构的时候,m_str对象内部指针没有被引用,删除时恰好发现指针不合法,引起问题。
m_str = str.c_str();可以避免引用。   回复  更多评论
  
# re: std::string一个极其隐晦得问题
2006-07-26 15:51 | 爱上小白

有一个不成文但是却是重要的前提, STL对象尽量不要作为dll的接口传递.

不过你说的问题应该是不存在的, 因为
SetString(std::string str)
{
m_str = str; //这里++ref
}

所以结果str还是存在一个ref供外部调用的.
  回复  更多评论
  
# re: std::string一个极其隐晦得问题
2008-06-12 10:45 | 小潘
我这几天也遇到这个问题,此问对我有很多帮助,非常感谢,发现用STL对象的确有很多问题,最好改用const char * 如果需要然后在进行转换。  回复  更多评论
  
# re: std::string一个极其隐晦得问题
2011-07-30 07:48 | hls
不是stl本身有问题,而是stl的微软实现有问题,如果都用stlport就可以解决问题,因为微软判断一个字符串是否没有分配内存不是拿指针是否为NULL来比较,而是把它和一个全局的“null”字符串来比较,这样由于跨module后,出现了两个“null”字符串,所以导致错误。微软的实现比较的垃圾。  回复  更多评论
  

只有注册用户登录后才能发表评论。
【推荐】超50万行VC++源码: 大型组态工控、电力仿真CAD与GIS源码库
网站导航: 博客园   IT新闻   BlogJava   知识库   博问   管理


简历下载
联系我

<2006年4月>
2627282930311
2345678
9101112131415
16171819202122
23242526272829
30123456

常用链接

留言簿(66)

随笔分类

随笔档案

相册

搜索

  •  

最新评论

阅读排行榜

评论排行榜