注册 登录  
 加关注
   显示下一条  |  关闭
温馨提示!由于新浪微博认证机制调整,您的新浪微博帐号绑定已过期,请重新绑定!立即重新绑定新浪微博》  |  关闭

还东国的博客

行之苟有恒,久久自芬芳

 
 
 

日志

 
 

(转载)shared_ptr 在 stl容器中排序的陷阱  

2014-01-15 14:41:19|  分类: C++(VC)编程 |  标签: |举报 |字号 订阅

  下载LOFTER 我的照片书  |

shared_ptr stl容器中排序的陷阱

分类: C/C++/VC 2012-01-14 00:14 1006人阅读 评论(1) 收藏 举报

cjavalistobjectclassnull

实例代码:

[cpp] view plaincopyprint?

// test1.cpp : Defines the entry point for the console application. 

// 

 

#include "stdafx.h" 

#include <string.h> 

#include <string> 

#include <memory> 

#include <list> 

#include <iostream> 

 

 

 

using namespace  std; 

using namespace  std::tr1; 

 

class CInt: public enable_shared_from_this<CInt> 

public: 

    CInt( int _value):value_(_value){} 

    ~CInt(){} 

 

    int GetValue() { return value_;} 

 

    bool operator==( const CInt & other ) const  

    { 

        return value_ == other.value_; 

    } 

 

    bool operator < ( const CInt & other ) const  

    { 

        return value_ < other.value_; 

    } 

protected: 

private: 

    int value_; 

}; 

 

 

bool SPIntCmp( shared_ptr<CInt> & c1, shared_ptr<CInt> & c2) 

    return c1 < c2; 

 

bool SPIntValueCmp( shared_ptr<CInt> & c1, shared_ptr<CInt> & c2) 

    return *c1 < *c2; 

 

 

int _tmain(int argc, _TCHAR* argv[]) 

     

    list< shared_ptr<CInt> > ilist; 

 

    ilist.push_back( shared_ptr<CInt>( new CInt(1))); 

    ilist.push_back( shared_ptr<CInt>( new CInt(3))); 

    ilist.push_back( shared_ptr<CInt>( new CInt(5))); 

    ilist.push_back( shared_ptr<CInt>( new CInt(7))); 

    ilist.push_back( shared_ptr<CInt>( new CInt(9))); 

    ilist.push_back( shared_ptr<CInt>( new CInt(2))); 

    ilist.push_back( shared_ptr<CInt>( new CInt(4))); 

    ilist.push_back( shared_ptr<CInt>( new CInt(6))); 

    ilist.push_back( shared_ptr<CInt>( new CInt(8))); 

    ilist.push_back( shared_ptr<CInt>( new CInt(10))); 

 

    cout<<"Before sort"<<endl; 

    for ( auto it = ilist.begin(); it != ilist.end(); it++) 

    { 

        shared_ptr<CInt> i = *it; 

        cout<< i->GetValue()<<" "; 

    } 

    cout<<endl; 

 

    ilist.sort(); 

    cout<<"after default sort"<<endl; 

    for ( auto it = ilist.begin(); it != ilist.end(); it++) 

    { 

        shared_ptr<CInt> i = *it; 

        cout<< i->GetValue()<<" "; 

    } 

 

    cout<<endl; 

    ilist.sort(SPIntCmp); 

    cout<<"after SPIntCmp sort"<<endl; 

    for ( auto it = ilist.begin(); it != ilist.end(); it++) 

    { 

        shared_ptr<CInt> i = *it; 

        cout<< i->GetValue()<<" "; 

    } 

    cout<<endl; 

    ilist.sort(SPIntValueCmp); 

    cout<<"after SPIntValueCmp sort"<<endl; 

    for ( auto it = ilist.begin(); it != ilist.end(); it++) 

    { 

        shared_ptr<CInt> i = *it; 

        cout<< i->GetValue()<<" "; 

    } 

    cout<<endl; 

 

    return 0; 

输出:

Before sort

1 3 5 7 9 2 4 6 8 10

after default sort

9 2 4 6 8 10 1 3 5 7

after SPIntCmp sort

9 2 4 6 8 10 1 3 5 7

after SPIntValueCmp sort

1 2 3 4 5 6 7 8 9 10

请按任意键继续. . .

 

从结果可以看到调用 list.sort() list.sort(SPIntCmp)方法输出结果是一样的。都是错误的结果。

只有调用list.sort(SPIntValueCmp)的输出结果还是真确的。

这是因此 listsort()是调用容器对象的 operator<方法,容器对象是什么?这里容器对象是 shared_ptr<CInt> 而不是CInt,因此并不会调用 CIntoperator <方法进行比较,而是调用shared_ptr<CInt>operator<方法进行比较。查看shared_ptr源代码后,发现 shared_ptr的默认实现方式是根据资源的指针地址进行比较的,因此我们无法得到正确的排序结果。

对于此事,我们必须定制自己的Compare方法。在SPIntCmp方法中,我们调用的还是 shared_ptroperator <方法,因此效果和list.sort()的结果是一样的。

SPIntValueCmp中,我们使用的是shared_ptr管理的资源的值进行比较(这边代码省略了null ptr的判断处理,实际代码需要加上),此事调用的才是 CInt对象的operator<方法,因此能得到正确的结果。

 

如果 list<CInt *> sort()结果应该和list<shared_ptr<CInt> >的结果都是一样不可预料的。

 

根本原因是shared_ptr是按照引用来比较的而不是引用的值进行比较的,因此在使用shared_ptr时,对于在逻辑语意上相等的对象,一定要从同一个数据中构造shared_ptr对象,而不要从一个新的一模一样的副本中构造shared_ptr

例如

shared_ptr<CInt> c1( new CInt(1) );

shared_ptr<CInt> c1_1( new CInt(1));

c1 == c1_1 的结果是 false,而不是true,而 c1 < c1_1 结果并不一定是false,而是由CInt对象的地址决定,因此他们的引用不是同一个对象,虽然他们的引用是相等的2个对象。应该像下面这样使用

shared_ptr<CInt> c1( new CInt(1) );

shared_ptr<CInt> c1_1( c1);

是否感觉到shared_ptr很像JavaObject对象。Java对象默认也是根据引用来比较的,如果要根据对象的语意进行相等比较必须重载 equal方法。可惜shared_ptr没有提供这样的方式让我们可以直接比较shared_ptr受控对象的语意相等性。只能选择根据地址来比较。

最后说明:还有一个就是对于指针做为  std::map或者 std::set中的KEY时,一定要小心最后其退化成链表,达不到应有的效果。另外如果使用对象做KEY,还要对  < 和=两个操作符进行重载,因为排序和相等,要自己完成,否则会编译不过去。

  评论这张
 
阅读(793)| 评论(0)
推荐 转载

历史上的今天

在LOFTER的更多文章

评论

<#--最新日志,群博日志--> <#--推荐日志--> <#--引用记录--> <#--博主推荐--> <#--随机阅读--> <#--首页推荐--> <#--历史上的今天--> <#--被推荐日志--> <#--上一篇,下一篇--> <#-- 热度 --> <#-- 网易新闻广告 --> <#--右边模块结构--> <#--评论模块结构--> <#--引用模块结构--> <#--博主发起的投票-->
 
 
 
 
 
 
 
 
 
 
 
 
 
 

页脚

网易公司版权所有 ©1997-2017