6

我有一个类,它包含一个包含boost::shared_ptrs另一个类的对象的列表。

允许访问列表中元素的类成员函数返回原始指针。为了保持一致性,我还希望能够使用原始指针而不是 shared_ptrs 进行迭代。因此,当我取消引用列表迭代器时,我想获取原始指针,而不是shared_ptr.

我假设我需要为此编写一个自定义迭代器。这个对吗?如果是这样,有人可以指出我正确的方向 - 我以前从未这样做过。

4

3 回答 3

5

这是使用Boost transform_iterator的选项:

#include <list>
#include <boost/iterator/transform_iterator.hpp>
#include <tr1/memory>
#include <tr1/functional>

using std::list;
using std::tr1::shared_ptr;
using boost::transform_iterator;
using boost::make_transform_iterator;
using std::tr1::mem_fn;
using std::tr1::function;

struct Foo {};

struct Bar
{
  typedef shared_ptr< Foo > Ptr;
  typedef list< Ptr > List;
  typedef function< Foo* (Ptr) > Functor;
  typedef transform_iterator< Functor, List::iterator > Iterator;

  Iterator begin()
  {
    return make_transform_iterator( fooptrs.begin(), mem_fn( &Ptr::get ) );
  }

  Iterator end()
  {
    return make_transform_iterator( fooptrs.end(), mem_fn( &Ptr::get ) );
  }

  List fooptrs;
};

C++11 可以很容易地消除function包装器,但我没有方便的编译器来测试它。Iterator如果您看到需要,您还可以隐藏使用类型擦除的具体类型(我认为 Adob any_iterator​​e 为此提供了免费的类模板。)

于 2012-04-05T21:39:20.153 回答
1

我有时会看到人们使用 STL 容器,boost::shared_ptr但实际上不太明显且相对鲜为人知的容器boost::ptr_container可能是更好的选择。

这可能是也可能不是其中一种情况,但考虑到ptr_container类的一个很好的属性是它们的迭代器有一个“额外的”间接性,这有助于保持事情的清洁和安全。

于 2012-04-05T23:24:04.553 回答
0

‍♀️ ...僵尸线程...

如果您不能使用 Boost(例如,它是您的公共界面的一部分,并且您不想对您的用户提出要求),您可以相对轻松地推出自己的。对于一个容器my_container——它可能是一个标准、boost、任何容器的前端,但它的实际实现隐藏在你的实现文件中并且不暴露给你的 API——持有任何指向类的智能指针my_type,它会是这样的:

class iterator
{
public:
    ~iterator();

    iterator( const iterator& other );
    iterator& operator=( const iterator& other );

    // The standard iterator traits (names must be lower-case)
    typedef std::forward_iterator_tag iterator_category;
    typedef my_type*                  value_type;
    typedef std::ptrdiff_t            difference_type;
    typedef value_type*               pointer;
    typedef value_type                reference;

    iterator& operator++();
    iterator& operator++( int );
    reference operator*() const;

    friend bool operator==( const iterator& it1, const iterator& it2 );
    friend bool operator!=( const iterator& it1, const iterator& it2 );

private:
    // Private, type-erased construction
    friend class my_container;
    explicit iterator( void* );

    // Implementation hidden by pimpl
    struct impl;
    impl* _impl;
};

在 cpp 文件中,假设我们在后台使用 a vectorof shared_ptr

// Define the Pimpl struct
struct iterator::impl
{
    typedef std::vector< std::shared_ptr<my_type> >::iterator iterator;
    iterator iter;
};

// Use void* as type erasure to hide the actual types from the user
iterator::iterator( void* wrappedIter )
    : _impl( new impl( *reinterpret_cast<impl::iterator*>( wrappedIter ) ) )
{
}

// Copying
iterator::iterator( const iterator& other )
    : _impl( new impl( *other._impl ) )
{}

iterator& iterator::operator=( const iterator& other )
{
    _impl->iter = other._impl->iter;
    return *this;
}

// ... could implement moving too ...

iterator::~iterator() 
{
    // Destroy the pimpl
    delete _impl;
}

iterator& iterator::operator++()
{
    ++_impl->iter;
    return *this;
}

iterator& iterator::operator++( int )
{
    ++_impl->iter;
    return *this;
}

iterator::reference iterator::operator*() const
{
    // This is where the magic happens: We convert the shared_ptr to a raw pointer.
    return _impl->iter->second.get();
}

bool operator==( const iterator& it1, const iterator& it2 )
{
    return *it1 == *it2;
}

bool operator!=( const iterator& it1, const iterator& it2 ) 
{ 
    return !( it1 == it2 ); 
}

那么您所需要的只是一种创建这些的方法。为此,my_container/begin()函数end()看起来像:

my_container::iterator my_container::begin()
{
    // The & allows our type erasure by invoking our void* constructor
    auto iter = _data->vector.begin();
    return iterator( &iter );
}

my_container::iterator my_container::end()
{
    // The & allows our type erasure by invoking our void* constructor
    auto iter = _data->vector.end();
    return iterator( &iter );
}

这种类型擦除和获取成员变量的地址看起来有点冒险,但没关系,因为我们会立即重新解释void*和取消引用并复制它。

于 2019-11-29T22:01:27.653 回答