2009-05-08 36 views
28

Tôi có một lớp TContainer là tổng hợp của một số bộ sưu tập stl sưu tập tới lớp TItems.Iterator tùy chỉnh trong C++

Tôi cần phải tạo một Iterator để duyệt qua các phần tử trong tất cả các bộ sưu tập trong lớp TContainer của tôi, trừu tượng hóa máy khách của các hoạt động bên trong.

Điều gì sẽ là một cách hay để thực hiện việc này ?. Tôi có nên crate một lớp mở rộng một iterator (nếu như vậy, những gì iterator lớp tôi nên mở rộng), tôi nên tạo một lớp iterator đó là một tổng hợp của iterators?

Tôi chỉ cần trình lặp lặp FORWARD_ONLY.

tức là, Nếu đây là container của tôi:

typedef std::vector <TItem*> ItemVector; 
class TContainer { 
    std::vector <ItemVector *> m_Items; 
}; 

Điều gì sẽ là một Iterator tốt để đi qua tất cả các mục chứa trong các vectơ của biến thành viên m_Items.

+0

Bạn có thể cho chúng tôi biết thêm về container và iterator của bạn? Ví dụ, là iterator bi-directional? – joshdick

+0

Cảm ơn, tôi đã chỉnh sửa câu hỏi của tôi để làm rõ câu hỏi của bạn. – Sugar

+0

Bạn thực sự muốn m_items là một vector của con trỏ? Tại sao không chỉ là một vector của ItemVector? –

Trả lời

31

Khi tôi đã làm trình lặp của riêng mình (một thời gian trước bây giờ), tôi được kế thừa từ std :: iterator và chỉ định kiểu làm tham số mẫu đầu tiên. Hy vọng rằng sẽ giúp.

Đối với người chuyển tiếp chuyển tiếp, người dùng forward_iterator_tag thay vì input_iterator_tag trong mã sau.

Lớp này ban đầu được lấy từ lớp istream_iterator (và được sửa đổi để sử dụng riêng của tôi để nó có thể không giống với istram_iterator nữa).

template<typename T> 
class <PLOP>_iterator 
     :public std::iterator<std::input_iterator_tag,  // type of iterator 
           T,ptrdiff_t,const T*,const T&> // Info about iterator 
{ 
    public: 
     const T& operator*() const; 
     const T* operator->() const; 
     <PLOP>__iterator& operator++(); 
     <PLOP>__iterator operator++(int); 
     bool equal(<PLOP>__iterator const& rhs) const; 
}; 

template<typename T> 
inline bool operator==(<PLOP>__iterator<T> const& lhs,<PLOP>__iterator<T> const& rhs) 
{ 
    return lhs.equal(rhs); 
} 

Kiểm tra tài liệu này trên thẻ iterator:
http://www.sgi.com/tech/stl/iterator_tags.html

Vừa đọc lại các thông tin về vòng lặp:
http://www.sgi.com/tech/stl/iterator_traits.html

Đây là cách cũ làm việc (iterator_tags) các cách tiếp cận hiện đại hơn là thiết lập iterator_traits <> cho trình lặp của bạn để làm cho nó hoàn toàn tương thích với STL.

+3

Chuyên môn của iterator_traits của bạn tương thích hơn với STL như thế nào so với kế thừa từ std :: iterator? –

+9

'std :: iterator' có các đối số mặc định cho các kiểu tham chiếu và kiểu con trỏ, vì vậy bạn không phải gõ chúng trong ví dụ của bạn. Và làm cho lớp của bạn kế thừa từ 'std :: iterator' sẽ tự động chuyên' iterator_traits' cho bạn, do đó, nó là _the cách hiện đại của việc viết vòng lặp. –

6

Trình lặp là một lớp hỗ trợ giao diện nhất định. Ở mức tối thiểu, bạn sẽ muốn để có thể:

  • increment và/hoặc giảm nó
  • dereference nó để có được những đối tượng đó là "điểm" để
  • kiểm tra nó cho sự bình đẳng và bất bình đẳng
  • bản sao và chỉ định nó

Khi bạn có một lớp học có thể làm điều đó một cách hợp lý cho bộ sưu tập của mình, bạn sẽ cần phải sửa đổi bộ sưu tập để có chức năng trả về trình lặp.Ở mức tối thiểu, bạn sẽ muốn

  • một bắt đầu() hàm trả về một thể hiện của loại iterator mới của bạn đặt ở phần tử đầu tiên
  • một chức năng end() trả về một iterator là (có thể notionally) đặt ở một mục vào cuối mục trong vùng chứa của bạn
+0

Hơi phức tạp hơn một chút. Thêm Iterator_traits <> vào danh sách của bạn và bạn đã hoàn tất. –

+1

Bạn có thể sử dụng trình lặp mà không có các đặc điểm. –

+0

Tôi biết - nhưng nhiều người không bao giờ sử dụng các thuật toán đó. Tôi nghĩ rằng tốt hơn cho mọi người để xây dựng một phiên bản không phải mẫu đầu tiên, không kế thừa từ std :: iterator, và sau đó templatising nó sau khi họ nhận được tất cả mọi thứ làm việc. Những người khác có thể không đồng ý, tất nhiên. –

22

Nếu bạn có quyền truy cập Boost, sử dụng iterator_facade là giải pháp mạnh mẽ nhất và sử dụng khá đơn giản.

18

Đầu tiên chúng ta hãy khái quát một chút:

typedef int value_type; 
typedef std::vector<value_type*> inner_range; 
typedef std::vector<inner_range*> outer_range; 

Bây giờ iterator:

struct my_iterator : std::iterator_traits<inner_range::iterator> 
{ 
    typedef std::forward_iterator_tag iterator_category; 

    my_iterator(outer_range::iterator const & outer_iterator, 
       outer_range::iterator const & outer_end) 
    : outer_iterator(outer_iterator), outer_end(outer_end) 
    { 
     update(); 
    } 

    my_iterator & operator++() 
    { 
     ++inner_iterator; 
     if(inner_iterator == inner_end) 
     { 
      ++outer_iterator; 
      update(); 
     } 
     return *this; 
    } 

    reference operator*() const 
    { 
     return *inner_iterator; 
    } 

    bool operator==(my_iterator const & rhs) const 
    { 
     bool lhs_end = outer_iterator == outer_end; 
     bool rhs_end = rhs.outer_iterator == rhs.outer_end; 
     if(lhs_end && rhs_end) 
      return true; 
     if(lhs_end != rhs_end) 
      return false; 
     return outer_iterator == rhs.outer_iterator 
      && inner_iterator == rhs.inner_iterator; 
    } 

private: 

    outer_range::iterator outer_iterator, outer_end; 
    inner_range::iterator inner_iterator, inner_end; 

    void update() 
    { 
     while(outer_iterator != outer_end) 
     { 
      inner_iterator = (*outer_iterator)->begin(); 
      inner_end = (*outer_iterator)->end(); 
      if(inner_iterator == inner_end) 
       ++outer_iterator; 
      else 
       break; 
     } 
    }  
}; 

lớp này giả định hơn so với vòng lặp bên ngoài chứa con trỏ đến các dãy bên trong, đó là một yêu cầu trong câu hỏi của bạn. Điều này được phản ánh trong thành viên update, trong các mũi tên trước begin()end(). Bạn có thể thay thế các mũi tên này bằng dấu chấm nếu bạn muốn sử dụng lớp này trong tình huống phổ biến hơn nơi trình lặp bên ngoài chứa các phạm vi bên trong theo giá trị. Lưu ý BTW rằng lớp này là bất khả tri với thực tế là phạm vi bên trong chứa con trỏ, chỉ có khách hàng của lớp sẽ cần phải biết điều đó.

Mã có thể ngắn hơn nếu chúng tôi sử dụng boost::iterator_facade nhưng không nhất thiết phải thêm phụ thuộc tăng cho một thứ đơn giản như vậy. Bên cạnh đó, các phần khó khăn duy nhất là sự bình đẳng và các hoạt động gia tăng, và chúng ta phải viết mã những thứ đó.

Tôi đã rời khỏi thành viên nồi hơi-tấm sau là "bài tập cho người đọc":

  • increment postfix iterator
  • operator =
  • constructor mặc định
  • nhà khai thác>

Một bài tập thú vị khác là biến điều này thành một mẫu làm việc với các thùng chứa tùy ý. Mã cơ bản giống nhau ngoại trừ việc bạn phải thêm chú thích typename ở một vài nơi.

Ví dụ về sử dụng:

int main() 
{ 
    outer_type outer; 
    int a = 0, b = 1, c = 2; 
    inner_type inner1, inner2; 
    inner1.push_back(&a); 
    inner1.push_back(&b); 
    inner2.push_back(&c); 
    outer.push_back(&inner1); 
    outer.push_back(&inner2); 

    my_iterator it(outer.begin(), outer.end()); 
       e(outer.end(), outer.end()); 
    for(; it != e; ++it) 
     std::cout << **it << "\n"; 
} 

nào in:

0

Đây là mã đơn giản nhất tôi đã có thể sản xuất (đối với vòng lặp tùy chỉnh). Lưu ý rằng tôi chỉ mới bắt đầu khám phá khu vực này. Hàm này gọi hàm upper_bound được tích hợp sẵn để thực hiện tìm kiếm nhị phân trên hàm nguyên, x^2 làm ví dụ.

#include <algorithm> 
#include <iostream> 

using namespace std; 

class Iter 
{ 
    public: 
    int x; 
    Iter() { x = -1; } 
    Iter(int a) { x = a; } 

    bool operator!=(Iter &i2) const { return x != i2.x; } 
    void operator++() { x++; } 
    void operator+=(int b) { x += b; } 
    int operator-(const Iter &i2) const { return x - i2.x; } 
    int operator*() const { 
    cout << "calculating for x " << x << endl; 
    return x*x; 
    } 

    typedef random_access_iterator_tag iterator_category; 
    typedef int value_type; 
    typedef int difference_type; 
    typedef int* pointer; 
    typedef int& reference; 
}; 

main() 
{ 
    ios::sync_with_stdio(false); 
    cout << upper_bound(Iter(0), Iter(100), 40).x << endl; 
} 

// :collapseFolds=1:folding=explicit: 

Và đây là cách đầu ra trông giống như:

calculating for x 50 
calculating for x 25 
calculating for x 12 
calculating for x 6 
calculating for x 9 
calculating for x 8 
calculating for x 7 
7