代码之家  ›  专栏  ›  技术社区  ›  DrColossos

在一组指针中查找指针的内容

  •  2
  • DrColossos  · 技术社区  · 15 年前

    编辑 :看错了线,当然是一组,谢谢你发现我(懒惰)的错误。

    以下示例代码:

    set<Foo *> set_of_foos;
    
    set_of_foos.push_back(new Foo(new Bar("x")));
    set_of_foos.push_back(new Foo(new Bar("y")));
    [...]
    
    //the way a "foo" is found is not important for the example
    bool find_foo(Foo *foo) {
     return vector_of_foos.end() != vector_of_foos.find(s)
    }
    

    现在当我这样做的时候

    find_foo(new Foo(new Bar("x")));
    

    因为找不到它,所以它当然返回false。原因很明显(指针指向不同的对象,因为它们都被 new 导致地址的不同值)。

    我想比较一下 Foo (上例中的“x”)不是 Foo* 本身。使用Boost并不是修改foo的一个选项。

    我需要循环通过每个 Foo * 里面 set_of_foos 还是有更简单的解决方案?我试图唯一地将每个 并替换 set<Foo *> 用一个 map<string, Foo *> 但这似乎是一个非常“黑客”的解决方案,而且效率不高。

    附言:如果有人有更好的头衔,请更改。我很难用几句话来描述这个问题。

    6 回复  |  直到 9 年前
        1
  •  2
  •   eq-    15 年前

    find_foo(new Foo(new Bar("x"))); 听起来不是一个好主意-它很可能(在任何情况下)导致使用该搜索功能的内存泄漏。

    如果函数为:

    struct comparator {
        Foo* local;
        comparator(Foo* local_): local(local_) {}
        ~comparator() { /* do delete if needed */ } 
        bool operator()(const Foo* other) { /* compare local with other */ }
    };
    
    bool found = vec.end() != std::find_if(vec.begin(), vec.end(), comparator(new Foo(...)));
    
        2
  •  5
  •   Svisstack    15 年前

    改变你 vector set 与您的自定义可比较函数进行比较 Foo 物体。

    应该是:

    struct ltFoo
    {
      bool operator()(Foo* f, Foo* s) const
      {
        return f->value() < s->value();
      }
    };
    
    set<Foo*, ltFoo> sFoo;
    sFoo.insert(new Foo(new Bar("x"));
    sFoo.insert(new Foo(new Bar("y"));
    
    if (sFoo.find(new Foo(new Bar("y")) != sFoo.end())
    {
        //exists
    }
    else
    {
        //not exists
    }
    
        3
  •  1
  •   Roger Pate    15 年前

    我需要循环遍历每个foo*内部向量,还是有一个更简单的解决方案?

    您确实需要循环来找到您想要的,但是您可以使用std::find兘if或另一个“包装循环”。在C++ 0x中,LAMBDAS是比较自然的,但是在C++ 03中,我只使用正则循环,如果需要在一个以上的地方做这个,可能会被封装在自己的函数中。

        4
  •  1
  •   Nim    15 年前

    不要使用std::find,而是使用std::find_if并提供自己的谓词。当然,这依赖于您能够访问在foo中保存“x”的成员。

    struct FooBar
    {
      FooBar(Foo* search) : _search(search){}
      bool operator(const Foo* ptr)
      {
        return ptr->{access to member} == _search->{access to member};
      }
    
      Foo* _search;
    }
    
    vector<Foo*>::iterator it = std::find_if(vec.begin(), vec.end(), FooBar(new Foo(new Bar("x")));
    

    如果您不能访问成员,并且可以保证所有其他成员都是相同的,那么您可以在上面的函数中尝试一个裸memcmp,而不是“==”。

        5
  •  0
  •   Diego Sevilla    15 年前

    您也可以考虑使用 Boost Ptr container library . 它允许使用标准算法、查找等创建指针列表,就像它包含对象一样,并在向量删除时自动释放指针使用的内存。

        6
  •  0
  •   Benjamin Kay    9 年前

    我也有同样的问题,最后写了一个简单的解引用复合类来完成这项工作。我很想知道别人怎么看这个。问题的关键在于,现有的答案要求程序员使用您的集合以一种不寻常的方式访问它,这种方式很容易泄漏内存,即通过将临时地址传递给 std::set::find() 或通过 std::find_if() . 如果要以非标准方式访问标准容器,那么使用它有什么意义?Boost有一个很好的容器库来解决这个问题。但是由于在C++ 14中引入了透明比较器,所以可以编写自定义比较器。 std::set::insert() std::set:find() 按预期工作,不依赖于提升。你可以把它当作 std::set< Foo*, DereferenceCompare<Foo, YourFooComparator> > set_of_foos;

    #ifndef DereferenceCompare_H
    #define DereferenceCompare_H
    
    #include <type_traits>
    
    // Comparator for std containers that dereferences pointer-like arguments.
    // Useful for containers of pointers, smart pointers, etc. that require a comparator.
    // For example:
    //   std::set< int*, DereferenceCompare<int> > myset1;
    //   int myint = 42;
    //   myset1.insert(&myint);
    //   myset1.find(&myint) == myset.end(); // false
    //   myset1.find(myint) == myset.end(); // false
    //   myset1.find(42) == myset.end(); // false
    //   myset1.find(24) == myset.end(); // true, 24 is not in the set
    //   std::set<int*> myset2;
    //   myset2.insert(&myint); // compiles, but the set will be ordered according to the address of myint rather than its value
    //   myset2.find(&myint) == myset.end(); // false
    //   myset2.find(a) == myset.end(); // compilation error
    //   myset2.find(42) == myset.end(); // compilation error
    //
    // You can pass a custom comparator as a template argument.  It defaults to std::less<T>.
    // The type of the custom comparator is accessible as DereferenceCompare::compare.
    // For example:
    //   struct MyStruct { int val; };
    //   struct MyStructCompare { bool operator() (const MyStruct &lhs, const MyStruct &rhs) const { return lhs.val < rhs.val; } };
    //   std::set< MyStruct*, DereferenceCompare<MyStruct, MyStructCompare> > myset;
    //   decltype(myset)::key_compare::compare comparator; // comparator has type MyStructCompare 
    template< typename T, class Compare = std::less<T> > class DereferenceCompare
    {
    #if __cplusplus==201402L // C++14
    private:
      // Less elegant implementation, works with C+=14 and later.
      template<typename U> static constexpr auto is_valid_pointer(int) -> decltype(*(std::declval<U>()), bool()) { return std::is_base_of<T, typename std::pointer_traits<U>::element_type>::value || std::is_convertible<typename std::remove_cv<typename std::pointer_traits<U>::element_type>::type, T>::value; }
      template<typename U> static constexpr bool is_valid_pointer(...) { return false; }
    
    public:
      template<typename U, typename V> typename std::enable_if<is_valid_pointer<U>(0) && is_valid_pointer<V>(0), bool>::type operator() (const U& lhs_ptr, const V& rhs_ptr) const { return _comparator(*lhs_ptr, *rhs_ptr); } // dereference both arguments before comparison
      template<typename U, typename V> typename std::enable_if<is_valid_pointer<U>(0) && !is_valid_pointer<V>(0), bool>::type operator() (const U& lhs_ptr, const V& rhs) const { return _comparator(*lhs_ptr, rhs); } // dereference the left hand argument before comparison
      template<typename U, typename V> typename std::enable_if<!is_valid_pointer<U>(0) && is_valid_pointer<V>(0), bool>::type operator() (const U& lhs, const V& rhs_ptr) const { return _comparator(lhs, *rhs_ptr); } // dereference the right hand argument before comparison
    #elif __cplusplus>201402L // Better implementation, depends on void_t in C++17.
    public:
      // SFINAE type inherits from std::true_type if its template argument U can be dereferenced, std::false otherwise.
      // Its ::value member is true if the type obtained by dereferencing U, i.e. the pointee, is either derived from T or convertible to T.
      // Its ::value is false if U cannot be dereferenced, or it the pointee is neither derived from nor convertible to T.
      // Example:
      //   DereferenceCompare<int>::has_dereference; // std::false_type, int cannot be dereferenced
      //   DereferenceCompare<int>::has_dereference<int>::is_valid_pointee; // false, int cannot be dereferenced
      //   DereferenceCompare<int>::has_dereference<int*>; // std::true_type, int* can be dereferenced to int
      //   DereferenceCompare<int>::has_dereference<int*>::is_valid_pointee; // true, dereferencing int* yields int, which is convertible (in fact, the same type as) int
      //   DereferenceCompare<int>::has_dereference< std::shared_ptr<int> >::is_valid_pointee; // true, the pattern also works with smart pointers
      //   DereferenceCompare<int>::has_dereference<double*>::is_valid_pointee; // true, double is convertible to int
      //   struct Base { }; struct Derived : Base { }; DereferenceCompare<Base>::has_dereference<Derived*>::is_valid_pointee; // true, Derived is derived from Base
      //   DereferenceCompare<int>::has_dereference<Derived*>; // std::true_type, Derived* can be dereferenced to Derived
      //   DereferenceCompare<int>::has_dereference<Derived*>::is_valid_pointee; // false, cannot convert from Derived to int nor does Derived inherit from int
      template< typename, class = std::void_t<> > struct has_dereference : std::false_type { static constexpr bool is_valid_pointee = false; };
      template< typename U > struct has_dereference< U, std::void_t<decltype(*(std::declval<U>()))> > : std::true_type { static constexpr bool is_valid_pointee = std::is_base_of<T, typename std::pointer_traits<U>::element_type>::value || std::is_convertible<typename std::remove_cv<typename std::pointer_traits<U>::element_type>::type, T>::value; };
    
      template<typename U, typename V> typename std::enable_if<has_dereference<U>::is_valid_pointee && has_dereference<V>::is_valid_pointee, bool>::type operator() (const U& lhs_ptr, const V& rhs_ptr) const { return _comparator(*lhs_ptr, *rhs_ptr); } // dereference both arguments before comparison
      template<typename U, typename V> typename std::enable_if<has_dereference<U>::is_valid_pointee && !has_dereference<V>::is_valid_pointee, bool>::type operator() (const U& lhs_ptr, const V& rhs) const { return _comparator(*lhs_ptr, rhs); } // dereference the left hand argument before comparison
      template<typename U, typename V> typename std::enable_if<!has_dereference<U>::is_valid_pointee && has_dereference<V>::is_valid_pointee, bool>::type operator() (const U& lhs, const V& rhs_ptr) const { return _comparator(lhs, *rhs_ptr); } // dereference the right hand argument before comparison
    #endif
    
    public:
      typedef /* unspecified --> */ int /* <-- unspecified */ is_transparent; // declaration required to enable polymorphic comparisons in std containers
      typedef Compare compare; // type of comparator used on dereferenced arguments
    
    private:
      Compare _comparator;
    };
    
    #endif // DereferenceCompare_H