2009-11-23 40 views
13

Tôi có một đối tượng mà tôi muốn đi du lịch trong một vòng lặp liên tục trong một trò chơi. Tôi có một loạt tọa độ trong một số std::vector mà tôi muốn sử dụng làm điểm tham chiếu.Cách dễ nhất để tạo một trình vòng lặp tuần hoàn (bộ tuần hoàn)?

Có cách nào để tạo std::vector<T>::iterator tuần hoàn (còn được gọi là người lưu hành) không? Điều tốt nhất tôi có thể đưa ra là có hai vòng lặp và sau đó bất cứ khi nào trình vòng lặp đầu tiên bị kiệt sức gán cho nó giá trị của giá trị thứ hai (sẽ không được sử dụng để làm bất kỳ điều gì khác) nhưng tôi thậm chí không chắc chắn sẽ làm việc - liệu toán tử gán có sao chép bất cứ thứ gì mà trình vòng lặp đang sử dụng để giữ chỉ mục hay nó sẽ chỉ được tham chiếu (và do đó sẽ vô dụng sau vòng thứ hai)?

Tôi muốn đối tượng di chuyển điểm tham chiếu mãi mãi (trừ khi nó bị hủy nhưng điều đó không xảy ra trong phương thức đó), nhưng trình lặp sẽ chỉ được gọi một lần cho mỗi khung và phải quay lại để tôi có thể cập nhật các đối tượng trong trò chơi.

Giải pháp phải hoạt động trên trình biên dịch gcc và microsoft (nếu không thể viết nó theo chuẩn C++).

+0

Tôi đã viết một trình lặp như vậy nên chắc chắn nó có thể =) Biến duy nhất tôi nhớ là toán tử so sánh

+0

Mặt khác, bạn có thực sự cần 'toán tử <'? Đó là một chút tinh nghịch để ẩn hành vi cyclic! –

Trả lời

21

Ok, bây giờ vấn đề của bạn là rõ ràng hơn :-)

Hãy nhìn vào boost :: iterator_facade và đẩy mạnh :: adaptor iterator. Họ thực hiện các giao diện iterator đầy đủ và cycle_iterator chỉ là để thực hiện một vài phương pháp của bạn như increment(), giảm một():

template<class IteratorBase> 
class cycle_iterator 
    : public boost::iterator_adaptor< 
      cycle_iterator,  // the derived class overriding iterator behavior 
      IteratorBase,  // the base class providing default behavior 
      boost::use_default, // iterator value type, will be IteratorBase::value_type 
      std::forward_iterator_tag, // iterator category 
      boost::use_default // iterator reference type 
     > 
{ 
    private: 
    IteratorBase m_itBegin; 
    IteratorBase m_itEnd; 

    public: 
    cycle_iterator(IteratorBase itBegin, IteratorBase itEnd) 
     : iterator_adaptor_(itBegin), m_itBegin(itBegin), m_itEnd(itEnd) 
    {} 

    void increment() { 
     /* Increment the base reference pointer. */ 
     ++base_reference(); 

     /* Check if past-the-end element is reached and bring back the base reference to the beginning. */ 
     if(base_reference() == m_itEnd) 
      base_reference() = m_itBegin; 
    } 

    // implement decrement() and advance() if necessary 
    }; 

này có lẽ không biên dịch nhưng nên giúp bạn bắt đầu.

Edit:

boost::iterator_adaptor cài đặt giao diện iterator đầy đủ về vài chức năng. Nó cung cấp triển khai mặc định cho increment(), decrement(), advance(), distance_to(), equal_to()dereference() bằng trình lặp cơ sở được chuyển xuống lớp cơ sở iterator_adaptor.

Nếu tất cả những gì bạn cần là một trình chuyển tiếp tiến, chỉ cần thực hiện phương thức increment() để bọc xung quanh khi bạn đến trình vòng lặp kết thúc. Trình lặp tuần hoàn có thể là hai chiều nếu bạn thực hiện decrement() theo cách tương tự. Nếu IteratorBase chính nó là một trình vòng lặp truy cập ngẫu nhiên, trình vòng lặp vòng lặp cũng có thể là truy cập ngẫu nhiên và phương thức advancedistance_to phải được thực hiện bằng cách sử dụng các phép toán modulo.

+0

+1: 'iterator_adaptor' là cách dễ nhất để viết vòng lặp, và các ví dụ chỉ là tuyệt vời (đặc biệt là làm thế nào để chỉ viết một adapter và có được cả hai phiên bản const và không const) –

+0

Có cách nào để làm cho các toán tử nhị phân như '! =' để làm việc với các đối số của kiểu 'cycle_iterator ' và 'IteratorBase'? Tôi đang cố gắng so sánh iterator 'cycle_iterator' và" bình thường "và cần phải thực hiện quá tải rõ ràng trong trường hợp này. – Mikhail

+1

'increment()' cần làm việc methinks - Tôi nghĩ lý tưởng là tăng giá trị trước khi 'kết thúc' sẽ cho bạn' begin'ning, không phải 'kết thúc' trên một vòng lặp chu kỳ. – Yakk

-5

Lấy được bộ sưu tập của riêng bạn từ std :: vector và cung cấp việc thực hiện trình lặp của riêng bạn ghi đè số tăng thêm & toán tử giảm.

Có rất nhiều hướng dẫn trên web. Ví dụ, hãy nhìn vào this blog post

+3

xuất phát từ vectơ? Bạn đã bao giờ _try_ đó? Tôi không đồng ý. Tổng hợp thay vào đó! – xtofl

+0

Tôi KHÔNG BAO GIỜ thấy một lớp học có nguồn gốc từ một thùng chứa tiêu chuẩn tốt hơn cho nó. Tổng hợp. –

+1

Tôi đồng ý với nhận xét. Đáp ứng được đăng trong vội vàng, ăn năn lúc rảnh rỗi. Tuy nhiên, không thể xóa nó vì nó đã được chấp nhận như một câu trả lời. –

7

boost::iterator adaptor là con đường để đi, dùng từ ngữ của tôi cho nó;)

Điều đó đang được nói rằng tôi muốn chỉ ra một vài cạm bẫy. Tôi không nghĩ rằng tôi có thể chỉnh sửa một câu trả lời hiện có, vì vậy chịu với tôi.

Cho rằng trình lặp cơ sở của bạn sẽ là một vectơ, bạn cần phải cẩn thận các chức năng giao diện cốt lõi nào bạn cần thực hiện.Nếu bạn muốn cycle_iterator của bạn trở thành một iterator truy cập ngẫu nhiên bạn cần tất cả các nội dung sau:

increment() 
decrement() 
advance(n) 
distance_to(j) 

Bây giờ distance_to(j) là một khái niệm có phần hài hước cho một cycle_iterator và ngữ nghĩa của nó có thể giúp bạn có được trong tất cả các loại rắc rối. Điều này có thể tránh được bằng cách hạn chế danh mục vòng lặp của trình lặp lặp thích nghi về phía trước hoặc hai chiều. Như thế này:

template <class BaseIterator> 
class cycle_iterator 
    : public boost::iterator_adaptor< 
     cycle_iterator     // Derived 
     , BaseIterator     // Base 
     , boost::use_default    // Value 
     , boost::forward_traversal_tag // CategoryOrTraversal 
    > 
{ ... }; 

Trong trường hợp này bạn chỉ cần thực hiện increment:

void increment() 
{ 
    if (++this->base_reference() == this->m_itEnd) 
    { 
    this->base_reference() = this->m_itBegin; 
    } 
} 

Đối với một hai chiều bạn cũng cần sụt lần:

void decrement() 
{ 
    if (this->base_reference() == this->m_itBegin) 
    { 
    this->base_reference() = this->m_itEnd; 
    } 
    --this->base_reference(); 
} 

Disclaimer: Tôi không chạy này thông qua một trình biên dịch, vì vậy tôi đã sẵn sàng để được xấu hổ.

+0

Tôi nghĩ rằng nói chung 'distance_to (j)' có thể trả về một số dương hoặc một số âm, khi được thêm vào một iterator, sinh ra một iterator trỏ đến 'j'. Bạn có thể quyết định rằng bạn muốn trả về một trong hai số nhỏ nhất không phải là số âm, hoặc số đó có giá trị tuyệt đối nhỏ nhất. Trong hầu hết các trường hợp, sau này có thể có ý nghĩa nhất - hoặc có một lập luận mạnh mẽ nếu không (một counterexample)? –

Các vấn đề liên quan