본문 바로가기
C++/기초

C++ 기초 : iterator (2)

글: 시플마 2024. 4. 20.

iterator도 직접 구현을 해 보겠습니다.

 

표준 라이브러리에서 제공하는 iterator는

클래스 안에 존재하는 inner 클래스였죠?

 

vector(가변 배열)와 같은 기능을 하는

클래스 템플릿을 작성한 cArr.h 파일로 가서

inner 클래스 iterator를 작성하겠습니다.

 

 

cArr.h에

 

클래스 iterator를 추가했습니다.

 

long long형 멤버 m_ll가 있습니다.

 

이때 64Bit 환경에서 클래스 cArr의 크기는 몇 Byte일까요?

cArr의 멤버는 포인터 하나와 int형 멤버 두 개가 있네요.

포인터는 환경 크기와 같으므로 64Bit 환경에서 8Byte입니다.

여기에 int가 두 개이므로 8 + 4 + 4 = 16Byte입니다.

 

여기에 iterator 클래스는 long long 멤버 m_ll이 있습니다.

long long은 8Byte이죠. 해당 클래스가 내부에 존재하므로 

16 + 8 = 24Byte일까요?

 

아닙니다. 

 

cArr 클래스 내부에 iterator 클래스가 있지만 

둘은 엄연히 다른 클래스입니다. 

 

cArr 클래스를 통해 객체를 만들었다고 해서

해당 객체가 iterator 클래스의 멤버 m_ll을

갖고 있는 것은 아니니까요.

 

cArr 클래스를 통해 만든 객체의 크기는 16Byte입니다.

 

반대로 iterator 클래스를 통해

만든 객체의 크기는 8Byte겠죠.

 

 

또한

vector<int>::iterator;

vector<float>::iterator;

vector<short>::iterator;

 

위와 같은 코드가 있다면

각 iterator는 전혀 다른 클래스입니다.

 

같은 vector 클래스 템플릿이더라도 

vector<int> 클래스와

vector<float> 클래스와 

vector<short> 클래스는 다른 것이니까요.

 


 

 

 

이제 본격적으로iterator 클래스를 구현합시다.

 

cArr 클래스 내부의 iterator 클래스는 

가변 배열의 특정 인덱스를 가리키기 위한 클래스이죠?

 

그럼 iterator가 있어야 하는 것은 두 가지입니다.

시작 주솟값을 저장할 T형 포인터 m_pData와

몇 번째 인덱스인지 표시할 int형 변수 idx입니다.

 

cArr는 클래스 템플릿이기 때문에 

어떤 자료형이든 저장할 수 있는 vector(가변 배열)입니다.

내부에 있는 iterator 클래스도 어떤 자료형의 데이터든

가리킬 수 있어야겠죠. 그래서 T형 포인터로 선언한 것입니다.

 

 

iterator 클래스를 통해 만든 객체는

vector의 시작 주솟값을 알고,

시작 주솟값으로부터 몇 번째 인덱스인지

알고 있다면 모든 인덱스에 접근이 가능하죠.

 

만약 아래 그림처럼

 

iterator가 2번 인덱스를 가리키게 하고 싶다면

시작 주솟값에 2를 더하면 되니까요.

 

 

37 ~ 42번째 줄을 보시면

생성자와 소멸자를 만들었습니다.

 

생성자를 통해 멤버 m_pData는 초기에

아무것도 가리키지 않기 때문에 nullptr로,

멤버 idx는 아무런 인덱스에도 접근하지 

않았다는 의미로 -1로 초기화하겠습니다.

 


 

 

 

이제 cArr 클래스에 inner 클래스 iterator를

 

만들었으니 main 함수에서 위 코드처럼

cArr<int> 클래스 내부의

iterator 클래스를 통해 객체를 만들 수 있게 되었습니다.

 

iterator 클래스를 통해 myiter라는 객체를 만들었고

이제 myVector이 가리키는 배열의 시작 주솟값을 받으려고 하는데 

직접 구현한 vector에는 begin 함수가 없어서 

오류가 발생하네요.

 

begin 함수를 구현해 봅시다.

 

27번째 줄에 begin 함수를 추가했습니다.

 

begin 함수를 통해 반환한 값을

iterator에 대입하려면 begin 함수가 반환하는 것은

iterator 자료형이어야겠죠?

 

이러한 이유로 반환 타입을 iterator로 하였고

iterator 클래스가 아래에 선언되어 있어서 

27번째 줄에서는 iterator가 뭔지 모릅니다.

 

그래서 26번째 줄에서 보이는 것처럼

iterator 클래스가 존재한다고

먼저 선언을 해줘야 합니다.

 

물론 27번째 줄의 코드를

iterator 클래스 밑에 써 주면

26번째 줄을 작성할 필요가 없습니다.

 

 

아래 코드는 begin 함수의 정의입니다.

 

cArr<T> 클래스 내부의 iterator 클래스가 반환 타입임을

알려 줘야 하기 때문에 typename이라는 키워드를 붙여야 합니다.

 

그리고 cArr<T> 클래스의 멤버 함수 begin를 정의한다는 의미입니다.

 

해당 함수에 iterator형 지역 변수 iter를 선언합니다.

그리고 vector의 시작 주솟값을 저장하는

iter의 멤버 m_pData에 

vector의 시작 주솟값을 저장하고 있는

cArr<T>형 객체의 m_pData를 저장합니다.

 

123번째 줄에서 오른쪽에 있는 m_pData는 앞에 

this->가 생략되어 있는 것이겠죠?

해당 함수를 호출한 cArr형 객체가 가리키는 vector의

시작 주솟값을 좌항에 대입하는 것이니까요.

 

 

시작 주솟값을 저장한 후에 124번째 줄에서

현재 몇 번째 인덱스인지를 의미하는

iter의 멤버 idx에 0을 넣습니다. 시작 주솟값을

저장했다는 것은 0번째 인덱스임을 의미하니까요.

 

 

근데 이렇게 쓰고 나니 생성자로 처리해도 될 거 같습니다.

 

그래서 생성자를 하나 더 추가했습니다.

 

T형 포인터와 정수를 하나 받았을 때 

호출되는 생성자이죠.

 

해당 생성자가 호출되면 iterator의

멤버 m_pData에 T형 포인터가 저장하고 있는

주솟값을 저장하고 멤버 idx에는 정수로 받은

값을 저장하죠.

 

 

이렇게 생성자를 만들고 나면

 

begin 함수를 위 코드처럼 더 간략하게 작성할 수 있습니다.

 

iterator 클래스를 통해 iter라는 객체를 만들고

cArr형 객체가 가리키는 vector의 시작 주솟값과

0을 인자로 넘겨서 iter를 초기화하고 이를 반환하는 것이죠.

 

 

이를 더 간략하게 쓸 수 있습니다.

 

어차피 지역 변수 iter를 만들고 초기화하자마자 반환하기 때문에

애초에 이름도 짓지 않고 초기화를 진행한

임시 객체를 반환하는 것입니다.

 

 

begin 함수를 구현했으니

 

main 함수에서 사용해 보겠습니다.

 

아까와 같은 코드인데

추가로 myVector에 10, 20, 30을 넣었습니다.

 

실행해 보니 오류 없이 잘 실행되는 것을 확인할 수 있습니다.

 

실행하고 나면 myiter에는

myVector의 시작 주솟값,

즉 0번째 인덱스의 주솟값이 저장되어 있을 겁니다.

 

 

 


 

 

 

여기서 끝이 아니죠?

 

26 ~ 27번째 줄에 있는 코드에서 볼 수 있듯이

실제 표준 라이브러리에서 제공하는 함수인 

vector를 통해 iterator를 사용할 때에도

iterator에 다양한 연산이 가능했습니다.

 

* 연산을 통해 인덱스로 접근하여

값을 얻을 수 있었고, 대입까지 가능했죠.

 

 

또한 증감(++, --) 연산을 통해

다음 또는 이전 인덱스로 손쉽게 접근이 가능했습니다.

 

 

아래 코드처럼 

 

list를 통해 노드에 접근하여

노드의 저장된 값을 출력하기 위해

for 문을 사용할 때 비교 연산도 가능한 것이 보입니다.

 

 

따라서 직접 구현한 vector와 같은 기능을 하는

cArr 클래스의 내부에 있는 iterator 클래스에도 

이러한 연산자 오버로딩이 되어 있어야 하는 것입니다.

 

 

특히 증감 연산자는

전위와 후위라는 개념도 

다 따로 구현해 주어야 합니다.

 

아래 코드 36번째 줄에서 보이듯이

 

idata에는 시작 주솟값을 저장한 myiter를 1 올렸기 때문에

20이 있는 인덱스를 가리키게 될 겁니다. 이 상태에서

역참조하여 값을 대입하기 때문에 20이 대입될 것이고,

 

 

아래 코드의 경우에는

 

시작 주솟값을 저장한 myiter를 먼저 역참조한 값을

idata에 대입하고 myiter를 1 올리기 때문에

10이 대입된 후 20이 있는 인덱스를 가리킬 겁니다.

 

 

 

 

강의 출처 : https://www.youtube.com/watch?v=PFc4g8mxOiI&list=PL4SIC1d_ab-aOxWPucn31NHkQvNPHK1D1&pp=iAQB


 

'C++ > 기초' 카테고리의 다른 글

C++ 기초 : iterator (4)  (0) 2024.04.22
C++ 기초 : iterator (3)  (0) 2024.04.22
C++ 기초 : iterator (1)  (0) 2024.04.20
C++ 기초 : STL (vector와 list)  (0) 2024.04.20
C++ 기초 : namespace와 입출력 구현 (cout, cin)  (0) 2024.04.19