zoukankan      html  css  js  c++  java
  • [zz]shared_ptr 在 stl容器中排序的陷阱

    实例代码:

    // 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)的输出结果还是真确的。

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

    对于此事,我们必须定制自己的Compare方法。在SPIntCmp方法中,我们调用的还是 shared_ptr的operator <方法,因此效果和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很像Java的Object对象。Java对象默认也是根据引用来比较的,如果要根据对象的语意进 行相等比较必须重载 equal方法。可惜shared_ptr没有提供这样的方式让我们可以直接比较shared_ptr受控对象的语意相等性。只能选择根据地址来比较。

  • 相关阅读:
    Mysql字段约束
    MYSQL中数据类型介绍
    Redis常见的几种使用方式及其优缺点
    python解析jason串,数据存入数据库
    redo log
    Oracle控制文件(Control Files)
    笔记
    redis面试题
    mysqldump备份
    超简单的内网穿透技巧(使用花生壳进行内网穿透)
  • 原文地址:https://www.cnblogs.com/zhangzhang/p/2877401.html
Copyright © 2011-2022 走看看