簡體   English   中英

C ++中的多態迭代器

[英]polymorphic iterators in C++

我正在嘗試在C ++中實現多態迭代器。 基本上,我需要這個能夠應用一個過濾器,以便迭代器可以跳過一些項目,具體取決於相關的條件。 所以我使用抽象接口創建了一個類似GoF的迭代器,這允許我從中派生一個過濾迭代器並實現所需的邏輯。 我也更喜歡基於接口的迭代器而不是模板化的迭代器,因為它們允許隱藏實現而不會導致一堆鴨類模板。

但是,多態迭代器不能通過值返回(與STL迭代器相反),所以我必須傳遞指針,這很容易變得危險,就像在這種情況下,這似乎是合乎邏輯的,但會導致內存泄漏:

Iter* Collection::GetIter() {...} // new IterImpl
DoSomething(Iter*) {...} // doesn't do delete

DoSomething(Collection.GetIter()); // convenient, but wrong :\

顯而易見的解決方案是使用某種智能指針來控制迭代器的生命周期,但人們常說接口應盡可能簡單和通用,因此應該避免使用智能指針?

如果你在C ++中使用過多態迭代器,那么這個問題是如何解決的? 或者基於模板的迭代器是C ++中唯一“好”的迭代方式? 謝謝。

通常的方法是使用編譯時多態而不是運行時多態; 這允許編譯器有更多機會使用迭代器優化代碼,並且通常在現代C ++中更慣用。

如果確實需要運行時多態行為,那么將多態性封裝在迭代器本身中並且不在外部公開它可能是最容易的。 您可以使用多態函數包裝像完成這個function ,在加速中發現,C ++ TR1和C ++ 0x中。 我在這里提供了一個基於我的一個業余愛好項目的過濾器迭代器的例子:

template <typename ForwardIt>
class filter_iterator
    : public std::iterator<
          std::forward_iterator_tag, 
          typename std::iterator_traits<ForwardIt>::value_type>

{
public:

    typedef typename std::iterator_traits<ForwardIt>::value_type ValueType;
    typedef typename std::function<bool(ValueType)> FunctionType;

    filter_iterator() { }

    explicit filter_iterator(ForwardIt end)
        : it_(end), end_(end) 
    {
    }

    filter_iterator(ForwardIt it, ForwardIt end, FunctionType is_filtered) 
        : it_(it), end_(end), is_filtered_(is_filtered)
    { 
        skip_filtered_elements(); 
    }

    const ValueType& operator*()  const { return it_.operator*();  }
    const ValueType* operator->() const { return it_.operator->(); }

    filter_iterator& operator++()   
    { 
        ++it_; skip_filtered_elements(); return *this; 
    }

    filter_iterator operator++(int) 
    { 
        filter_iterator it(*this); ++*this; return it; 
    }


    friend bool operator==(const filter_iterator& lhs,
                           const filter_iterator& rhs)
    {
        return lhs.it_ == rhs.it_;
    }

    friend bool operator!=(const filter_iterator& lhs,
                           const filter_iterator& rhs)
    {
        return !(lhs == rhs);
    }

private:

    void skip_filtered_elements()
    {
        while (it_ != end_ && is_filtered_(*it_))
            std::advance(it_, 1);
    }

    ForwardIt it_;
    ForwardIt end_;

    std::function<bool(const ValueType&)> is_filtered_;
};

template <typename ForwardIt>
filter_iterator<ForwardIt> make_filter_iterator(ForwardIt end)
{
    return filter_iterator<ForwardIt>(end);
}

template <typename ForwardIt, typename Function>
filter_iterator<ForwardIt> make_filter_iterator(ForwardIt it, 
                                                ForwardIt end, 
                                                Function f)
{
    return filter_iterator<ForwardIt>(it, end, f);
}

用法很簡單。 此示例(使用C ++ 0x lambda表達式作為函數類型)演示了從一個范圍中過濾奇數:

int main()
{
    std::array<int, 4> x = { 1, 2, 3, 4 };

    std::copy(make_filter_iterator(x.begin(), x.end(), [](int i) { return i % 2; }),
              make_filter_iterator(x.end()),
              std::ostream_iterator<int>(std::cout, " "));
}

這里有兩個問題:

  • 語法:STL假定迭代器提供需要與實際項匹配的traits( value_typereference )。
  • 語義:迭代器應該是可復制的。

請記住(在C ++中)迭代器不是范圍,因此++操作很快就會變得混亂,因為您需要跳過某些項目,但是(使用傳統實現)您無法知道有多少項目可供您使用。 ..

因此,如果您需要遵循GOF接口的多態迭代器,則必須放棄使用STL算法。

也就是說,實現多態迭代器是完全可行的:

struct IterBase
{
  virtual void increment() = 0;
  virtual void decrement() = 0;

  // others
};

class Iter
{
public:
  Iter& operator++() { base->increment(); return *this; }
  Iter operator++(int) { Iter tmp(*this); base->increment(); return tmp; }

  // others

private:
  std::unique_ptr<IterBase> base;
};

然后你需要編寫所有的復制構造函數,賦值運算符和析構函數來做正確的事情......

但是,如果沒有模板多態,只有你的迭代器只能用於同一類型時,它才有價值...

我看到一個很好的解決方案與Oli Charlesworth的問題相關聯,這個問題沒有得到太多的信任(至少,沒有我想象的那么多)。

class Iterator
{
public:
    SmartPointer<IteratorImplementation> ItrPtr;

    //Delegate methods to ItrPtr
}

然后,您可以按值傳遞Iterator並將方法推遲到包含的智能指針; 它基本上是一個實現“策略”模式的迭代器,策略展示了多態行為。

它可以使用包含某種形式的指針的迭代器來完成,然后將該功能傳遞給指針。

雖然這樣做你需要非常小心,我已經多次看到它做錯了(包括我自己摔倒一次,然后想知道為什么測試失敗......)

  • 不要使用shared_ptr!

幸運的是,我沒有看到上面的任何人犯了使用shared_ptr的錯誤,但它有錯誤的語義,就像復制迭代器時你有2個單獨的副本。 但是如果它們包含一個shared_ptr而你推進其中一個迭代器,那么另一個將隨之移動 - 意外的行為......

因此,每次復制時都需要克隆,但幸運的是,使用C ++ 0x,您可以移動大部分時間而不是克隆。

您還需要知道每次迭代時操作都會遇到v-table,這可能導致它運行速度比使“宏”方法變為多態(並且可能使用模板實現,因此您不需要重寫代碼) )。

人們會說接口應該盡可能簡單和通用。 在您的情況下,您將原始指針描述為不可能的東西。 因此,我建議您使用智能指針的明顯解決方案是最簡單和最通用的技術。

為了使這個智能指針盡可能簡單和通用,我會選擇一個stl提供的智能指針,因為它們是最普遍的。

到過那里。 做完了。

你可以做的是隱藏你的迭代器接口在另一個迭代器后面。 假設您有幾種全部隱藏在IIterator接口后面的迭代器。

然后編寫另一個類似迭代器的類,例如MyIterator,它包含一個指向IIterator的指針,它只是將所有調用轉發給IIterator,如下所示:

template <typename T>
class MyIterator
    {
    public:
       MyIterator() : m_iterator(nullptr) {}
       MyIterator(IIterator *it) : m_iterator(it) {}
       MyIterator &operator++()
          {
          if (m_iterator) m_iterator->operator++();
          return *this;
          }
       T &operator*() const
          {
          if (m_iterator) return m_iterator->operator*();
          else            throw an exception?
          }
    private
       IIterator *m_iterator;
    };

這個例子遠非完整,但你應該明白這個想法。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM