2010-01-20 2 views
4

저는 C++ STL 반복기의 사용법을 잘 알고 있습니다.이터레이터에 사용 된 패턴

for(map<pair<int,int>>::iterator it=m.begin(); it!=m.end(); ++it) 
    int a = it->first; 
    int b = it->second; 

하지만 내면의 세부적인 내용은 알지 못합니다. 어떤 사람들이 나에게 설명 할 수 있을까? C++, Java, C# 또는 Python.

+1

Iterator 개념은 C++과 Java/C#에서 서로 다릅니다. 두 경우 모두 증분 및 참조 취소가 가능한 객체이지만 작업 집합과 사용법은 다릅니다. –

답변

1
+0

감사합니다. 그러나 나는 UML에 익숙하지 않다. ... – user253951

+3

패턴에 관심이 있다면, UML에 대한 기본적인 지식을 가지고있는 것이 좋다. UML을 설명하는 언어는 프랑스어이다. –

+0

참조 된 기사는 반복기가 Java와 C#에서 어떻게 작동하는지에 대한 것입니다 (질문에 대한 것 같습니다) –

0

을 제외하고 반복자 패턴 : 당신이 게시 한 코드가 작동하지 않습니다 - 당신은 괄호 놓치고 -도 C 번호를, 나 C++이나 자바 마법 공백이 (그리고 그 코드는 정말 아무튼 파이썬처럼 보이지 않는다.)

또한 >>은 오른쪽 시프트 연산자로 해석됩니다 (감사 Prasoon Saurav). 당신이 이것을 원한다고 생각합니다 :

for(map<pair<int,int> >::iterator it=m.begin(); it!=m.end(); ++it) { // <- here 
    int a = it->first; 
    int b = it->second; 
} // <- and here 
+1

또한'>> '는 오른쪽 시프트 연산자로 해석됩니다. –

1

반복자 사용의 한 점은 "내부 세부 사항"에 대해 알 필요가 없다는 것입니다.

그러나 그 기본 원리를 이해하는 것이 항상 유용하며 사용자가 언급 한 모든 언어에서 동일합니다. 다양한 범위의 대상으로 작업해야하는 기능 (또는 언어 기능)이 있습니다. 그런 다음 함수는 반복자 객체를 가져 와서 Python을 사용하여 다음과 같이 할 수 있습니다.

def DoSomething(start_iter, end_iter): 
    while (start_iter != end_iter): 
     calculate(start_iter.get_data()) 
     start_iter = start_iter.next() 

원칙은 모든 경우에 동일합니다. 제네릭 함수는 반복자를 받아 들여 위에서 설명한대로 사용합니다. 반복자와 관련된 데이터를 가져 와서 무엇이든 수행하고 반복기를 증가 시키며 반복기가 끝나면 종료합니다.

예를 들어, C++에서 STL :: vector의 반복자는 단순한 정수 이상에 가까우며 순수한 배열을 반복하는 것과 같은 방식으로 반복 처리가 수행됩니다.

1

Iterator 패턴에 대한 위키 링크를 확인하십시오. 이 패턴의 목적은 내부 세부 사항을 노출시키지 않으면 서 집계 객체의 요소에 대한 액세스를 제공하는 것입니다 (다른 대답과 링크와 마찬가지로).

예를 들어, Java에서 AbstractList의 반복자는 List를 반복하기 위해 인스턴스가 생성 된 내부 클래스에서 구현됩니다. 여기에서 code을 확인하십시오.

2

iterator는 역 참조를 정의하는 추상 개념이며, * 연산자를 사용하면 반복자와 연결된 시퀀스의 특정 요소를 제공하고 증가하면 다음 요소와 연관된 반복기가 제공됩니다. 어떤 시퀀스. 즉, 구체적인 반복자는 시퀀스에 의해 정의되며 별도로 정의 된 클래스가 아니며, 즉 iterator이 아닌 map<pair<int,int> >::iterator 유형을 사용해야하는 이유입니다. 각 STL 시퀀스의 반복자 유형에는 시퀀스 자체의 다음 요소를 가리키는 반복자를 제공하기 위해 ++ 연산자가 오버로드되는 고유 한 구현이 있습니다.

이것은 포인터의 역 참조가 반복자 (포인터)와 연결된 객체를 제공 할 것이므로 증가하는 것이 포인터와 연관된 새로운 반복자 (포인터)를 줄 것이므로 문자 배열의 간단한 포인터가 반복자라는 것을 의미합니다. 순서의 다음의 요소

이중 연결리스트에 대한 부분적인 예는

class doubly_linked_list { 
    class node { 
    node* prev; 
    node* next; 
    int data; 
    node(const int data, node* prev, node* next) : data(data), prev(prev), next(next) {} 
    }; 
    static const node nil; // = node(0, 0, 0) 
    node* head; 
    node* tail; 
public: 
    class iterator { 
    node* current; 
    iterator(node* n) : current(n) {} 
    public: 
    int& operator*() const { 
     return current->obj; 
    } 
    iterator& operator++() { 
     current = current->next; 
     return *this; 
    } 
    iterator& operator--() { 
     current = current->prev; 
     return *this; 
    } 
    }; 
    double_linked_list() : head(nil), tail(nil) {} 
    iterator begin() const { 
    return iterator(head); 
    } 
    iterator end() const { 
    return iterator(tail); 
    } 
}; 

그리고 데이터 구조가 어떻게 다른 설명하기 : (참고 : 테스트되지 않은, 단지이 쓴 어떤 친구 조항과 물건을 추가해야 할 수 있습니다) 다른 반복자 벡터 예를 배열에 포인터를 닮은 용기에 C++ 반복자에서

class vector { 
    int* data; 
    size_t len; 
public: 
    typedef int* iterator; 
    vector(const size_t len) : len(len) { 
    data = new int[len]; 
    } 
    int& operator[](int i) const { 
    return data[i]; 
    } 
    iterator begin() const { 
    return data; 
    } 
    iterator end() const { 
    return data + len; 
    } 
}; 
7

(단지 안된로) (난 당신이 포인터를 잘 알고있는 것으로 가정하고). 이터레이터에는 여러 가지 맛이 있지만, 결국에는 (데 참조 연산자 *->을 통해) 컨테이너 내의 요소를 참조하고 컨테이너의 요소를 가로 지르는 방법 일뿐입니다.

중요한 부분은 구현이 아니라 오히려 개념입니다. 목록이나 벡터에 대한 반복자가 구현되는 방식 (또는 대부분의 경우에서 그 방식이 어떻게 다른지)을 알 필요가없고, 제공하는 연산 만 알면됩니다. 서로 다른 컨테이너에있는 반복자는 서로 다른 구현을 갖게 될 것입니다 (노드의 next 포인터 뒤에 오는 목록을 위해, right 자식 또는 parent 균형 트리의 포인터를 따라갈 것입니다. 실제로 동일한 컨테이너에있는 반복자를 구현할 수 있습니다 컴파일 플래그 나 모드에 따라 여러 가지 방법으로 (일부 컴파일러는 특정 컨테이너에 대해 두 가지 이상의 구현이 있습니다.) 그러나 중요한 부분은 실제로 어떻게 사용하는지 상관하지 않는다는 것입니다. .

//... 
class vector { 
    T * _b; // beginning of allocated memory 
    T * _e; // one past the last inserted element 
    T * _c; // one past the end of the allocated memory 
//... 
} 
:

간단한 예를 들어, g에서 ++의 STL 구현 std::vector 뭔가 같은 세 가지 포인터를 포함

이고, size() = (_e - _b)/sizeof(T)capacity = (_c - _b)/sizeof(T)이다. 벡터의이 구현으로, 당신은 반복자로 원시 포인터를 사용할 수 있습니다

//... 
class vector { 
public: 
    typedef T* iterator; 
    T* begin() { return _b; } 
    T* end() { return _e; } 
//... 
} 

하지만 반복자가있는 경우 주장을 트리거하는 반복자를 확인처럼 당신은 또한 더 복잡한 (느리지 만 안전) 반복자의 구현을 구축 할 수 있습니다 (이 코드는 단지 샘플 목적으로 과도하게 단순화되는) 무효화 :

template <typename T> 
class checked_iterator { 
public: 
    checked_iterator(std::vector<T> & v, std::size_t e) 
     : _check_begin(v._b), _vector(v), _pos(v._b + e) 
    {} 
    T& operator*() { 
     assert(_check_begin == _vector._b && "Dereferencing an invalidated iterator"); 
     return *_pos; 
    } 
    // ... 
private: 
    T * _pos; 
    T const * const _check_begin; 
    std::vector<T>& _vector; 
}; 

구현은 전체 벡터 이전의 경우 유효하지 않은 반복자를 (역 참조 감지 것이 반복자하지만, 더 많은 데이터가 전체를 할 수있는 저장하여 확인), 아직 개발 중에 잘못된 프로그램의 실행을 중단합니다. 사용자 관점에서 볼 때 일반 RandomAccessIterator (벡터 반복자에 대한 요구 사항) 일 수 있지만 장면 뒤에서는 오류를 감지하기 어렵다는 것을 식별하는 메커니즘을 제공합니다.

이것은 VS 컴파일러의 접근 방식입니다. 디버그 모드에서 (컴파일러 플래그에 따라) 무효화 된 반복기를 통한 액세스 감지에 도움이되는 느린 안전 반복기를 사용합니다 (벡터의 경우 반복자는 요소 컨테이너에 추가됨). 동시에 컴파일러 플래그를 변경하면 프로덕션 시스템에서 훨씬 빠른 일반 원시 포인터 구현을 얻을 수 있지만 유효하지 않은 반복자 사용을 디버그하는 것이 훨씬 어려울 수 있습니다.

자바와 C#에서

그들은 실제로 컨테이너가 구현되는 방법 전체 컨테이너 hidding의 횡단을 허용 (자바 hasNext(), next()remove()에서) 단순 작업의 몇 가지를 구현하는 객체입니다.그것들은 사용자 코드에서 특정 컨테이너에 대해 수행 된 작업을 캡슐화한다는 점에서 매우 유사합니다.

두 가지 중요한 차이점은 두 경우 모두 전체 컨테이너에서 반복 할 수 있지만 C++에서는 비교할 수 있으며 두 반복자를 반복하여 같은 컨테이너에 반복 할 수 있다는 것입니다. 예를 들어, 도시 전화 번호부가 포함 된지도에서 작업을 사용하여 ac로 시작하는 이름으로 반복자를 가져올 수 있고 'd'로 시작하는 이름의 첫 번째 요소를 가져올 수 있습니다 (이름 순서 지정 가정). 이 반복자를 사용하여 STL (또는 자신의) 알고리즘을 사용하여 해당 하위 집합에서만 작업을 수행 할 수 있습니다.

+0

+1, 훌륭한 답변! –

1

좋은 example (C#, MSDN)

표준 인터페이스 인 IEnumerable이 있습니다. 클래스가 열거 가능한 경우이 인터페이스의 GetEnumerator 메서드 만 구현하면됩니다.

그러나 알고 계시 겠지만, 다른 클래스는 열거하는 다른 방법이있을 수 있습니다. 배열의 경우 포인터를 1만큼 이동하면 트리에 대해 트리 탐색 방법이 필요합니다. 하지만 전체적으로 열거자는 동일한 인터페이스를가집니다. C#에서 IEnumerator의 메서드.

IEnumerable을 구현하는 클래스 외에도 여전히 IEnumerator를 구현하는 클래스, 구체적으로 MoveNext, Reset을 구현해야합니다. 그리고 GetEnumerator 메서드는 열거 자 클래스의 인스턴스를 반환합니다.

관련 문제