(C++) Iterator

Posted by : at

Category : Cpp


iterator는 왜 쓸까?

  • list와 vector에 있는 요소를 열거하는 방법은 다르다.(메모리구조로 인해)
  • 동일한 방법으로 요소에 접근할 수 있을까? -> iterator 적용
#include <vector>
#include <iostream>

using namespace std;

int main() {
	vector<int> vec = {1, 2, 3};
	for (auto it = vec.begin(); it != vec.end(); ++it) {
		cout << *it << endl;
	}
	
	// C++11 range-for loop
	for (auto v: vec) {
		cout << v << endl;
	}
}
#include <iostream>
#include <vector>

using namespace std;

int main() {
	vector<int> vec = {1, 2, 3};

	// Create an iterator which points at the first element in vec
	vector<int>::const_iterator it = vec.cbegin();
	
    // Create a const iterator which points at the last element in vec
	vector<int>::const_reverse_iterator it = vec.rbegin();

    // Create an iterator which points at the last element in vec
	vector<int>::reverse_iterator it = vec.rbegin();

	// C++11 alternative
	//auto it = vec.cbegin();
	
	while (it != vec.cend()) {          // Check whether the iterator is still valid
	    cout << *it << endl;            // Print out the data in the first element - displays 1
	    //*it = 6;                      // Compiler error! Cannot modify through const iterator
	    ++it;                           // Advance the iterator to the next element
	}
}
#include <iostream>
#include <vector>

using namespace std;

int main() {
	vector<int> vec = {1, 2, 3};                  // Create a vector

	auto ins_it = vec.begin();                    // Get an iterator to the first element
	++ins_it;                                     // Advance it to the second element
	
	auto it = inserter(vec, ins_it);              // Get an insert iterator for vec

	// Assign to this iterator
	*it = 99;                                     // Adds element with value 99 before second element
	*it = 88;                                     // Adds element 88 to vec, before the same element
	
	// vec  now contains {1, 99, 88, 2, 3}

	// Print out vector elements
	for (auto v: vec)
		cout << v << endl;
}
#include <iostream>
#include <vector>

using namespace std;

int main() {
	vector<int> vec;                        // Create an empty vector

	auto it = back_inserter(vec);           // Get an insert iterator for vec

	// Assign to this iterator
	*it = 99;                               // Adds element with value 99 to vec
	*it = 88;                               // Adds element 88 to vec, which now contains {99, 88}

	// Print out vector elements
	for (auto v: vec)
		cout << v << endl;
}

여기서 부터는 iterator구현기 … 필요하다면 보자

간략하게 iterator를 구현해보자면 아래와 같다(참고만 하자)

#include <iostream>
using namespace std;

template<typename T> struct Node
{
    T data;
    Node* next;
    Node(const T& d, Node* n) : data(d), next(n) {}
};

template<typename T> class slist
{
    Node<T>* head = 0;
public:
    void push_front(const T& n) { head = new Node<T>(n, head); }
    T front()                   { return head->data; }
};

int main()
{
    slist<int> s;

    s.push_front(10);
    s.push_front(30);
    s.push_front(40);
    s.push_front(50);
}

구현시작


// 반복자의 규칙
template<typename T> 
struct IEnumerator
{
    virtual ~IEnumerator() {}
    virtual bool MoveNext() = 0;
    virtual T& GetObject() = 0;
};

// slist의 반복자
template<typename T> class SlistEnumerator : public IEnumerator<T>
{
    Note<T>* current = 0;
public: 
    SlistEnumerator( Node<T>* p = 0 ) : current(p) {}

    virtual bool MoveNext() { 
        current = current->next; 
        return current; 
    }
    virtual T& GetObject() { return current->data; }
};

// 모든 컨테이너에서 반복자를 꺼낼 수 있어야 한다.
// 컨테이너가 지켜야하는 인터페이스
template<typename T> struct IEnumerable
{
    virtual ~IEnumerable() {}
    virtual IEnumerator<T>* GetEnumerator() = 0;
};

template<typename T> class slist : public IEnumerable<T>
{
    Node<T>* head = 0;
public:
    virtual IEnumerator<T>* GetEnumerator()
    {
        return new SlistEnumerator<T>(head);
    }
    void push_front(const T& n) { head = new Node<T>(n, head); }
    T front()                   { return head->data; }
};

int main()
{
    slist<int> s;

    s.push_front(10);
    s.push_front(20);

    IEnumerator<int>* p = s.GetDnumerator();

    int n = p->GetObject();
    p->MoveNext();
}
  • 인터페이스를 사용하지 않는다.
  • 이동 및 접근 함수는 포인터에 규칙에 따른다. (++, –, * 사용가능하게 만든다)
template<typename T> class slist_iterator
{
    Note<T>* current = 0;
public: 
    slist_iterator( Node<T>* p = 0 ) : current(p) {}

    inline slist_iterator& operator++()
    { 
        current = current->next; 
        return *this*; 
    }
    inline T& operator*() { return current->data; }
};
  • 컨테이너의 경우 지켜야하는 규칙을 담을 인터페이스를 없앤다.
  • 인터페이스는 없지만 약속된 함수인 begin멤버 함수로 반복자를 꺼낸다.
template<typename T> class slist
{
    Node<T>* head = 0;
public:
    slist_iterator<T> begin()
    {
        return slist_iterator<T>(head);
    }
    void push_front(const T& n) { head = new Node<T>(n, head); }
    T front()                   { return head->data; }
};
int main()
{
    slist<int> s;

    s.push_front(10);
    s.push_front(20);

    slist_iterator<int> p = s.begin();

    cout << *p << endl;
    ++p;
}

#include <iostream>
#include <list>
using namespace std;

int main()
{
    list<int> s = { 1, 2, 3, 4, 5 };

    // list<int>::iterator p = s.begin();
    // auto p = s.begin();
    auto p = begin(s);      // 이 구조가 제일 좋음 - C++11
    // 배열에도 먹히기 때문이다.

    int n = size(s);

    // 이거 주의
    auto p2 = end(s);
    *p2 = 10;       // error - end는 마지막 요소가 아니라 마지막 다음 요소임을 기억하자.
}

사용시 주의사항

#include <iostream>
#include <list>
#include <vector>
using namespace std;

int main()
{
    vector<int> v = {1,2,3,4,5};

    auto p = begin(v);

    v.resize(100);          // 버퍼 재할당

    cout << *p << endl;     // error - 버퍼가 재할당되며 무효화 된다.
}

range 활용

#include <iostream>
#include <list>
using namespace std;

int main()
{
    list<int> s1;

    if(s1.empty()) {}       // 요소가 비어있는지 확인
}
#include <iostream>
#include <list>
#include <algorithm>
using namespace std;

int main()
{
    int x[5] = {1,2,3,4,5};
    int y[5] = {0,0,0,0,0};

    list<int> s2 = {0,0,0,0,0};

    copy(x, x+5, y);
    copy(x, x+5, begin(s2));

    for( auto& h : y)
        cout << h << ", ";

    for( auto& h : s2)
        cout << h << ", ";
}

About Taehyung Kim

안녕하세요? 8년차 현업 C++ 개발자 김태형이라고 합니다. 😁 C/C++을 사랑하며 다양한 사람과의 협업을 즐깁니다. ☕ 꾸준한 자기개발을 미덕이라 생각하며 노력중이며, 제가 얻은 지식을 홈페이지에 정리 중입니다. 좀 더 상세한 제 이력서 혹은 Private 프로젝트 접근 권한을 원하신다면 메일주세요. 😎

Star
Useful Links