C++

2024.02.29 - iterator

강병곤 2024. 3. 3. 22:08

반복자(iterator)는 포인터와 같은 기능을 한다. 반복자는 컨테이너(vector, 배열, 등등)에 저장되어 있는 원소들을 참조할 때 사용한다. 반복자는 포인터처럼 일반적인 값이 아닌 주소값이 들어간다.

 

  • vector의 반복자 선언
vector<int>::iterator 반복자이름;

 

  • vector의 iterator begin(), end()

vector의 멤버 함수 begin()은 vector의 첫번째 요소의 반복자(주소값)를 반환한다. end()는 마지막 요소 +1의 반복자를 반환한다.

 

#include<iostream>
#include<vector>

int main() {
    std::vector<int>::iterator iter;
    std::vector<int> vec = { 1,2,3,4,5 };

    iter = vec.begin();
    std::cout << *iter << std::endl;

    iter = --vec.end();
    std::cout << *iter << std::endl;
}

 

반복자를 역참조해서 그 주소의 값을 참조할 수 있다. 여기서 주의할 점은 end()는 vector의 마지막 요소의 주소가 아닌 그보다 한칸 더 뒤의 주소를 반환한다는 것이다. 

 

 

  • 반복자를 이용해 for문 돌리기
#include<iostream>
#include<vector>

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

    for (std::vector<int>::iterator iter = vec.begin(); iter != vec.end(); iter++) {
        std::cout << *iter << std::endl;
    }
}

 

 

  • vector의 요소 삽입, 제거 하기 (insert(), erase())
#include<iostream>
#include<vector>

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

	std::vector<int>::iterator iter = vec.begin() + 1;
	vec.insert(iter, 7);

	for (std::vector<int>::iterator iter = vec.begin(); iter != vec.end(); iter++) {
		std::cout << *iter << std::endl;
	}
}

 

#include<iostream>
#include<vector>

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

	std::vector<int>::iterator iter = vec.begin() + 1;
	vec.erase(iter);

	for (std::vector<int>::iterator iter = vec.begin(); iter != vec.end(); iter++) {
		std::cout << *iter << std::endl;
	}
}

 

insert와 erase를 사용하면 해당 위치의 요소를 수정한뒤 다른 요소의 데이터를 일일히 한 칸 씩 옮겨준다. 그렇기 때문에 데이터의 삽입과 제거가 빈번히 일어날 경우 vector보단 배열이나 deque를 사용하는 것이 좋다.

 

 

  • 반복문 안에서 vector의 요소 삽입, 제거하기
#include<iostream>
#include<vector>

int main() {
    std::vector<int> v = { 1,2,3,4,5 };
    std::vector<int>::iterator iter = v.begin();
    for (int i = 0; i < v.size(); i++) {
        if (v[i] == 3) v.erase(iter+i);
    }
    for (int i = 0; i < v.size(); i++) {
        std::cout << v[i] << std::endl;
    }
}

 

반복문을 반복자를 사용하지 않고 돌리는 경우 erase를 하더라도 문제가 생기지 않는다.

하지만 반복자를 사용해서 반복문을 돌리는 경우 중간에서 값을 erase하면 문제가 생긴다.

 

#include<iostream>
#include<vector>

int main() {
    std::vector<int> v = { 1,2,3,4,5 };
    for (std::vector<int>::iterator iter = v.begin(); iter != v.end(); iter++) {
        if (*iter == 3) v.erase(iter);
    }
    for (int i = 0; i < v.size(); i++) {
        std::cout << v[i] << std::endl;
    }
}

can't increment invalidated vector iterator. 무효화된 벡터 반복기를 증가시킬 수 없습니다. 라는 에러가 뜬다. vector의 요소를 erase한 순간 부터 그 벡터의 반복자(iter)는 무효화 되어 증가시킬수 없게 된다. 이 경우 새로운 반복자를 iter에 대입시켜 주어야 반복문을 계속 돌릴 수 있다.

 

v.erase() 단순히 벡터 v의 요소를 지울 뿐 아니라, 지운 요소의 다음 요소의 주소값을 반환하는 기능이 있다.erase에서 반환되는 반복자 값을 iter에 대입 시켜주면 반복문이 잘 돌아간다.

 

#include<iostream>
#include<vector>

int main() {
    std::vector<int> v = { 1,2,3,4,5 };
    for (std::vector<int>::iterator iter = v.begin(); iter != v.end(); iter++) {
        if (*iter == 3)  iter = v.erase(iter);
    }
    for (int i = 0; i < v.size(); i++) {
        std::cout << v[i] << std::endl;
    }
}

 

vector에 값을 삽입할 때도 비슷한 방법으로 해결할 수 있지만 주의할 점이 있다.

#include<iostream>
#include<vector>

int main() {
    std::vector<int> v = { 1,2,3,4,5 };
    for (std::vector<int>::iterator iter = v.begin(); iter != v.end(); iter++) {
        std::cout << "for문을 영원히 돌거야..." << std::endl;
        if (*iter == 3)  iter = v.insert(iter, 7);
    }
    for (int i = 0; i < v.size(); i++) {
        std::cout << v[i] << std::endl;
    }
}

 

v.insert로 값 3이 있던 자리에 7을 넣고 그 다음 주소를 가리키면, 그 주소는 여전히 값3 인 곳을 가리키고 있으므로 for문을 빠져 나올 수 없다.

 

#include<iostream>
#include<vector>

int main() {
    std::vector<int> v = { 1,2,3,4,5 };
    for (std::vector<int>::iterator iter = v.begin(); iter != v.end(); iter++) {
        if (*iter == 3)  iter = ++v.insert(iter, 7);
    }
    for (int i = 0; i < v.size(); i++) {
        std::cout << v[i] << std::endl;
    }
}

 

insert가 반환하는 주소값에 +1 해주어야 다음 값을 비교할 수 있다.