치춘짱베리굿나이스

[Rank 5] ft_containers - std::reverse_iterator 본문

42/42s Cursus

[Rank 5] ft_containers - std::reverse_iterator

치춘 2023. 1. 17. 23:17

먼저 읽어보고 오세요

[Rank 5] ft_containers - std::iterator_traits

 

[Rank 5] ft_containers - std::iterator_traits

std::iterator_traits namespace ft { template struct iterator_traits { // template로 가져온 클래스에서의 :: 값은 typename으로 가져와야 한다 typedef typename Iter::difference_type difference_type; typedef typename Iter::value_type value_t

blog.chichoon.com

iterator_category 에서 양방향 반복자와 임의접근 반복자가 무엇인지 간단하게나마 알고 오면 조금 더 쉽다!!

뭐하는 친구인가요

42를 하는 한국인이라면 익숙할 노선도이다 (버스 타는 사람이나 대모산입구 쪽에서 오는 사람은 죄송)

이때 지하철을 반복자, 선릉 ~ 개포동분당선 컨테이너 내의 요소들이라고 생각해보자

  • 지하철은 선릉에서 출발하여 한티, 도곡, 구룡을 거쳐 개포동에 도착한다
    • = 반복자는 ‘선릉’ 요소에서 ‘개포동’ 요소까지의 범위 (range) 를 순회한다
    • 이 방향을 ‘순방향’ 이라고 가정하자
  • 하지만 집에 가고 싶을 땐 선릉에서 개포동 방향으로 나아가는 지하철을 탔다간 집과 정반대인 수원이나 인천 방향으로 빠져버린다
    • 이 때는 선릉에서 개포동으로 가는 열차 대신, 개포동에서 선릉으로 가는 열차를 타야 한다
    • 선릉에서 개포동으로 가는 열차가 정방향 반복자였다면, 개포동에서 선릉으로 가는 열차는 역방향 반복자 (reverse_iterator) 가 된다

특징

  • 역방향 반복자는 정방향 반복자와 반대 방향으로 동작한다
    • 증가 (+, ++) 연산자는 정방향 반복자의 시점에서 감소하는 방향으로 움직인다
    • 감소 (-, —-) 연산자는 정방향 반복자의 시점에서 증가하는 방향으로 움직인다
    • +, - 연산자도 마찬가지로 역방향으로 동작한다

 

  • 정방향 반복자의 시작점이 begin() 이었다면, 역방향 반복자의 시작점은 rbegin() 이다
  • 정방향 반복자의 끝점이 end() 였다면, 역방향 반복자의 끝점은 rend() 이다
  • begin()rend() 보다 한 칸 앞이다
  • end()rbegin() 보다 한 칸 뒤이다

멤버 변수와 생성자

기본 반복자

base iterator 라고도 한다

기본 반복자는 원래 정방향으로 움직이던 반복자로, 개포동 → 선릉 노선 (수서행) 을 만들기 위해 선릉 → 개포동 노선 (왕십리행) 을 뼈대 삼아 구축하는 것이라고 생각하면 된다

생성자 단계에서 인자로 받아, 내부적으로 복사하여 저장한다

복사된 반복자는 reverse_iterator 내부 멤버 함수를 호출할 때마다 가리키는 요소가 변경되는 실질적 객체이며, 결과적으로 역방향 반복자는 정방향 반복자를 복사한 뒤 동작만 거꾸로 한 것이라고 볼 수 있다

생성자

reverse_iterator(); // 기본 생성자

explicit reverse_iterator(iterator_type it); // 초기화 생성자

template <class Iter>
reverse_iterator(const reverse_iterator<Iter>& rev_it); // 복사 + 타입 캐스트 생성자
  • 기본 생성자
    • 어떠한 인자도 받지 않는 쌩 기본 생성자이다
    • 기본 반복자 (base_iterator) 가 초기화되지 않는다
  • 초기화 생성자
    • 기본 반복자를 인자로 받아 멤버 변수로 저장하는 역할을 한다
    • 강제 형변환을 막아주는 explicit 키워드가 사용되었다
  • 복사 생성자
    • 기존의 reverse_iterator를 복사한다
    • 기존의 반복자를 복사하므로, 내부의 멤버 변수인 기본 반복자도 복사된다

멤버 함수들

base

iterator_type base() const {
    return _base_it; // 기본이 되는 반복자 = base iterator 반환
}

기본이 되는 반복자를 반환한다

기본이 되는 반복자 (base iterator) 는 원래 정방향으로 움직이던 반복자를 생성자 단계에서 멤버 변수로 저장해둔 것이다

역방향 반복자도 내부적으로는 기본 반복자가 (역방향으로) 움직이는 방식으로 구현된다

 

(아님)

operator*

reference operator*() const;
  • * 연산자에 대한 오버로드
    • * 연산자는 요소의 참조를 반환할 때 사용된다
  • 클래스 내의 요소를 변경하지 않으므로 const가 붙는다
  • 구현을 할 때 주의해야 할 점으로
    • 겉으로 보기엔 rbegin() 이 정방향 반복자의 end() 보다 한칸 앞을 가리키는 것처럼 보인다
    • 허나 내부 구현상 정방향 반복자의 end() 가 역방향 반복자의 begin() (= rbegin()) 과 같기 때문에 실제로는 가리키고자 하는 요소의 한 칸 뒤를 가리키게 된다
    • 따라서 기본 반복자 (base iterator) 를 반환 전에 내부적으로 포인터 위치를 한 칸 앞으로 당겨주어야 한다는 것을 명심하자

operator→

pointer operator->() const;
  • -> 연산자에 대한 오버로드
    • -> 연산자는 요소의 참조를 반환할 때 사용된다
  • 클래스 내의 요소를 변경하지 않으므로 const가 붙는다
  • * 연산자와 마찬가지로 기본 반복자가 실제 요소의 한 칸 뒤를 가리키므로, * 연산자 오버로드를 호출하여 그 포인터를 가져오거나, 현재 기본 반복자의 한 칸 앞을 반환하는 방식으로 구현해야 한다

operator[]

reference operator[](difference_type n) const;
  • [] 연산자에 대한 오버로드
  • 특정 인덱스의 요소 (의 참조) 에 접근하고자 할 때 사용하는 연산자이다 (arr[1] 과 같은 식)
  • 마찬가지로 실제 인덱스에서 한 칸 앞의 요소를 반환해야 한다

operator++

reverse_iterator operator++();
  • ++ 연산자 (전위) 에 대한 오버로드
  • 역방향 연산자의 방향에 유의하여 구현하자

operator++ (int)

reverse_iterator operator++(int);
  • ++ 연산자 (후위) 에 대한 오버로드
  • CPP 02에서 후위연산자 오버로드를 처음 구현했을 때 반환값을 임시 저장했던 것을 기억하자
    • 후위연산이기 때문에, 해당 라인의 모든 다른 연산이 끝난 후 기본 반복자 (base iterator) 가 수정되어야 한다는 점에 유의

operator+=

reverse_iterator& operator+=(difference_type n);
  • += 연산자에 대한 오버로드
  • difference_type는 반복자가 가리키는 한 칸의 크기와 같다 (= 포인터가 가리키는 메모리 영역의 크기, 포인터를 한 칸 이동시켰을 때 실제로 이동하는 메모리의 크기)

operator+

reverse_iterator operator+(difference_type n);
  • + 연산자에 대한 오버로드

operator—

everse_iterator operator--()
  • -- 연산자 (전위) 에 대한 오버로드
  • ++ 연산자와 마찬가지로 역방향 반복자의 방향에 유의하자

operator—

reverse_iterator operator--(int);
  • -- 연산자 (후위) 에 대한 오버로드
  • ++ 연산자 (후위) 와 구현법이 동일하되 방향만 다르다

operator-=

reverse_iterator& operator-=(difference_type n) const;
  • -= 연산자에 대한 오버로드

operator-

reverse_iterator operator-(difference_type n) const;
  • - 연산자에 대한 오버로드

비 멤버 함수 오버로드

operator+

reverse_iterator<Iterator> operator+(typename reverse_iterator<Iterator>::difference_type n, const reverse_iterator<Iterator>& it);
  • 한 반복자를 n만큼 증가시킬 때 사용하는 덧셈 연산자 오버로드
  • 반복자가 연산자의 왼쪽에 있는 경우 (정수 n이 오른쪽) 는 멤버 함수 내에 + 연산자 오버로드로 구현하고, 정수가 연산자의 왼쪽에 있는 경우는 비 멤버 오버로드로 구현해야 한다

operator-

reverse_iterator<InputIt1>::difference_type operator-(const reverse_iterator<InputIt1>& lhs, const reverse_iterator<InputIt2>& rhs)
  • 두 반복자 lhs, rhs 간의 간격 (차이) 을 나타내는 뺄셈 연산자 오버로드
    • lhs: left-hand side, 연산자 기준 왼쪽
    • rhs: right-hand side, 연산자 기준 오른쪽

operator==

bool operator==(const reverse_iterator<InputIt1>& lhs, const reverse_iterator<InputIt2>& rhs);
  • 두 반복자 lhs, rhs의 같음 여부를 판단하는 등호 연산자 오버로드

operator!=

bool operator!=(const reverse_iterator<InputIt1>& lhs, const reverse_iterator<InputIt2>& rhs);
  • 두 반복자 lhs, rhs의 다름 여부를 판단하는 부등호 연산자 오버로드

operator<

bool operator<(const reverse_iterator<InputIt1>& lhs, const reverse_iterator<InputIt2>& rhs);
  • 두 반복자 lhs, rhs의 위치를 비교하는 부등호 연산자 오버로드
    • lhs가 더 클 경우 참이 된다 (역방향 반복자이므로)

operator<=

bool operator<=(const reverse_iterator<InputIt1>& lhs, const reverse_iterator<InputIt2>& rhs);
  • 두 반복자 lhs, rhs의 위치를 비교하는 부등호 연산자 오버로드
    • lhs가 더 크거나 같을 경우 참이 된다 (역방향 반복자이므로)

operator>

bool operator>(const reverse_iterator<InputIt1>& lhs, const reverse_iterator<InputIt2>& rhs);
  • 두 반복자 lhs, rhs의 위치를 비교하는 부등호 연산자 오버로드
    • lhs가 더 작을 경우 참이 된다 (역방향 반복자이므로)

operator>=

bool operator>=(const reverse_iterator<InputIt1>& lhs, const reverse_iterator<InputIt2>& rhs);
  • 두 반복자 lhs, rhs의 위치를 비교하는 부등호 연산자 오버로드
    • lhs가 더 작거나 같을 경우 참이 된다 (역방향 반복자이므로)

참고 자료

https://en.cppreference.com/w/cpp/container/vector

cppreference를 보십시오 휴먼

Comments