수색…


C 반복자 (포인터)

// This creates an array with 5 values.
const int array[] = { 1, 2, 3, 4, 5 };

#ifdef BEFORE_CPP11

// You can use `sizeof` to determine how many elements are in an array.
const int* first = array;
const int* afterLast = first + sizeof(array) / sizeof(array[0]);

// Then you can iterate over the array by incrementing a pointer until
// it reaches past the end of our array.
for (const int* i = first; i < afterLast; ++i) {
    std::cout << *i << std::endl;
}

#else

// With C++11, you can let the STL compute the start and end iterators:
for (auto i = std::begin(array); i != std::end(array); ++i) {
    std::cout << *i << std::endl;
}

#endif

이 코드는 다음과 같이 각 행에 하나씩 숫자를 출력합니다.

1
2

4
5

속보

const int array[] = { 1, 2, 3, 4, 5 };

이 행은 5 개의 값을 갖는 새로운 정수 배열을 만듭니다. C 배열은 각 값이 연속 블록에 함께 저장되는 메모리에 대한 포인터 일뿐입니다.

const int* first = array;
const int* afterLast = first + sizeof(array) / sizeof(array[0]);

이 행은 두 개의 포인터를 만듭니다. 첫 x 째 포인터에는 h 열 포인터의 값이 제공됩니다.이 값은 h 열의 첫 x 째 요소의 주소입니다. C 배열에서 sizeof 연산자를 사용하면 바이트의 배열 크기가 반환됩니다. 요소의 크기로 나눠서 배열의 요소 수를 제공합니다. 이를 사용하여 배열 다음 의 블록 주소를 찾을 수 있습니다.

for (const int* i = first; i < afterLast; ++i) {

여기서 우리는 iterator로 사용할 포인터를 생성합니다. 그것은 우리가 반복 할 첫 번째 요소의 주소로 초기화됩니다, 우리는만큼 반복 계속거야 i 미만 afterLast 만큼 의미, i 내 주소를 가리키는 array .

    std::cout << *i << std::endl;

마지막으로, 루프 내에서 우리는 값 우리의 반복자에 액세스 할 수 있습니다 i 그것을 역 참조가 가리키는됩니다. 역 참조 연산자 *i 의 주소에있는 값을 반환합니다.

개요

반복자는 위치입니다.

반복자는 일련의 요소를 탐색하고 조작하는 수단이며 포인터의 일반화 된 확장입니다. 개념적으로 iterator는 요소가 아니라 위치라는 것을 기억하는 것이 중요합니다. 예를 들어, 다음 순서대로 수행하십시오.

A B C

시퀀스는 3 개의 요소와 4 개의 위치를 ​​포함합니다.

+---+---+---+---+
| A | B | C |   |
+---+---+---+---+

요소는 시퀀스 내의 사물입니다. 위치는 의미있는 작업이 시퀀스에 발생할 수있는 장소입니다. 예를 들어, 하나는 요소가 아닌 요소 A의 이나 뒤에 삽입됩니다. 요소 삭제 ( erase(A) )는 먼저 위치를 찾아 지우면됩니다.

반복자에서 값으로

위치에서 값으로 변환하려면 반복기가 참조 해제됩니다 .

auto my_iterator = my_vector.begin(); // position
auto my_value = *my_iterator; // value

반복자는 시퀀스에서 참조하는 값을 역 참조하는 것으로 생각할 수 있습니다. 이는 end() iterator를 순차적으로 참조하지 않아야하는 이유를 이해하는 데 특히 유용합니다.

+---+---+---+---+
| A | B | C |   |
+---+---+---+---+
  ↑           ↑
  |           +-- An iterator here has no value. Do not dereference it!
  +-------------- An iterator here dereferences to the value A.

C ++ 표준 라이브러리에있는 모든 시퀀스와 컨테이너에서 begin() 은 반복자를 첫 번째 위치로 반환하고 end() 는 반복자를 마지막 위치가 아닌 마지막 위치로 반환합니다 (마지막 위치가 아닙니다 !). 결과적으로 알고리즘에서 이러한 반복자의 이름은 종종 firstlast 레이블이 지정됩니다.

+---+---+---+---+
| A | B | C |   |
+---+---+---+---+
  ↑           ↑
  |           |
  +- first    +- last

빈 시퀀스에도 최소한 하나의 위치가 포함되어 있기 때문에 모든 시퀀스에 대한 반복자를 얻을 수도 있습니다.

+---+
|   |
+---+

빈 시퀀스에서는 begin()end() 가 같은 위치에 있고 둘 다 역 참조 할 수 없습니다.

+---+
|   |
+---+
  ↑
  |
  +- empty_sequence.begin()
  |
  +- empty_sequence.end()

반복자의 다른 시각화는 요소 사이 의 위치를 ​​표시한다는 것입니다.

+---+---+---+
| A | B | C |
+---+---+---+
↑   ^   ^   ↑
|           |
+- first    +- last

반복기의 참조 해제는 반복자 뒤에 오는 요소에 대한 참조를 리턴합니다. 이보기가 특히 유용한 몇 가지 상황은 다음과 같습니다.

  • insert 조작은 요소를 반복자가 가리키는 위치에 삽입하고,
  • erase 조작은, 건네받은 조작과 같은 위치에 대응하는 반복자를 돌려줍니다.
  • 반복자와 그 대응하는 역 반복자 는 요소들 사이의 같은 위치에 위치한다.

유효하지 않은 반복자

iterator는 (작업 과정에서) 위치가 더 이상 시퀀스의 일부가 아닌 경우 무효화 됩니다. 무효화 된 반복자는 유효한 위치에 재 할당 될 때까지 참조 해제 될 수 없습니다. 예 :

std::vector<int>::iterator first;
{
    std::vector<int> foo;
    first = foo.begin(); // first is now valid
} // foo falls out of scope and is destroyed
// At this point first is now invalid

C ++ 표준 라이브러리의 많은 알고리즘과 시퀀스 멤버 함수에는 반복자가 무효화 될 때 적용되는 규칙이 있습니다. 각각의 알고리즘은 iterator를 다루는 (그리고 무효 화하는) 방식이 다릅니다.

반복자로 탐색하기

아시다시피 반복자는 시퀀스를 탐색하는 데 사용됩니다. 그렇게하기 위해서 반복자는 순서 전체에서 그 위치를 이동시켜야한다. 반복자는 순서대로 진행할 수 있고 일부는 뒤로 진행할 수 있습니다.

auto first = my_vector.begin();
++first;                                             // advance the iterator 1 position
std::advance(first, 1);                              // advance the iterator 1 position
first = std::next(first);                            // returns iterator to the next element
std::advance(first, -1);                             // advance the iterator 1 position backwards
first = std::next(first, 20);                        // returns iterator to the element 20 position forward
first = std::prev(first, 5);                         // returns iterator to the element 5 position backward
auto dist = std::distance(my_vector.begin(), first); // returns distance between two iterators.

, 표준의 두번째 인수 참고 : 거리가 첫 번째 행에 도달 할 수 있어야 (즉, 또는 first 작거나보다되어야 second ).

반복자를 사용하여 산술 연산자를 수행 할 수 있지만 모든 유형의 반복자에 대해 모든 연산이 정의되는 것은 아닙니다. a = b + 3; Random Access Iterators에서 작동하지만 Forward 또는 Bidirectional Iterators에서는 작동하지 않을 것입니다. Forward 또는 Bidirectional Iterator는 여전히 b = a; ++b; ++b; ++b; 와 같은 것으로 3 단계로 진행할 수 있습니다 b = a; ++b; ++b; ++b; . 따라서 iterator 유형이 확실하지 않은 경우 (예 : iterator를 허용하는 템플릿 함수에서) 특수 함수를 사용하는 것이 좋습니다.

반복자 개념

C ++ 표준은 여러 가지 반복기 개념을 설명합니다. 이들은 참조하는 순서대로 행동하는 방식에 따라 그룹화됩니다. 반복자가 모델링 한 개념 (동작이 유사 함)을 알고 있으면 해당 반복 순서에 관계없이 해당 반복자의 동작을 보장 할 수 있습니다. 그것들은 종종 다음 순서 반복자 개념이 전임자보다 더 나은 단계이기 때문에 가장 제한이 적은 순서대로 설명됩니다.

  • 입력 반복자 : 위치별로 한 번만 참조 해제 할 수 있습니다. 한 번에 한 포지션에서만 진행할 수 있습니다.
  • Forward Iterators : 반복적으로 역 참조 될 수있는 입력 반복자.
  • 양방향 반복자 : 한 번에 한 위치 씩 뒤로 이동할 수있는 앞으로 반복자.
  • 랜덤 액세스 반복자 (Random Access Iterators) : 한 번에 여러 포지션을 앞으로 또는 뒤로 이동할 수있는 양방향 반복자.
  • 인접 반복자 (C ++ 17 이후) : 기본 데이터가 메모리에서 연속적임을 보장하는 랜덤 액세스 반복자.

알고리즘은 주어진 반복자가 모델링 한 개념에 따라 달라질 수 있습니다. 예를 들어, 순방향 반복자에 대해 random_shuffle 을 구현할 수 있지만 임의 액세스 반복자를 필요로하는보다 효율적인 변형이 제공 될 수 있습니다.

반복자 특성

반복자 특성은 반복자의 속성에 대한 일관된 인터페이스를 제공합니다. 그들은 값, 차이, 포인터, 참조 유형 및 iterator의 범주를 검색 할 수 있습니다.

template<class Iter>
Iter find(Iter first, Iter last, typename std::iterator_traits<Iter>::value_type val)  {
    while (first != last) {
        if (*first == val)
            return first;
        ++first;
    }
    return last;
}

이터레이터의 범주는 알고리즘을 전문화하는 데 사용할 수 있습니다.

template<class BidirIt>
void test(BidirIt a, std::bidirectional_iterator_tag)  {
    std::cout << "Bidirectional iterator is used" << std::endl;
}
 
template<class ForwIt>
void test(ForwIt a, std::forward_iterator_tag)  {
    std::cout << "Forward iterator is used" << std::endl;
}
 
template<class Iter>
void test(Iter a)  {
    test(a, typename std::iterator_traits<Iter>::iterator_category());
}

반복자의 범주는 기본적으로 반복자 개념입니다. 단, 연속 반복자는 코드를 중단 한 것이므로 자체 태그가 없습니다.

역 반복기

목록이나 벡터를 거꾸로 반복하려는 경우 reverse_iterator 사용할 수 있습니다. 역방향 반복자는 base() 통해 액세스 할 수있는 멤버로 유지되는 양방향 또는 임의 액세스 반복기에서 만들어집니다.

역순으로 반복하려면 rbegin()rend() 를 콜렉션의 끝과 콜렉션의 시작에 대한 반복자로 사용하십시오.

예를 들어, 역순으로 반복하려면 다음을 사용하십시오.

std::vector<int> v{1, 2, 3, 4, 5};
for (std::vector<int>::reverse_iterator it = v.rbegin(); it != v.rend(); ++it)
{
    cout << *it;
} // prints 54321

reverse iterator는 base() 멤버 함수를 통해 forward iterator로 변환 될 수 있습니다. 관계는 역방향 반복기가 base() 반복자를지나 한 요소를 참조한다는 것입니다.

std::vector<int>::reverse_iterator r = v.rbegin();
std::vector<int>::iterator i = r.base();
assert(&*r == &*(i-1)); // always true if r, (i-1) are dereferenceable
                        // and are not proxy iterators

 +---+---+---+---+---+---+---+
 |   | 1 | 2 | 3 | 4 | 5 |   |
 +---+---+---+---+---+---+---+
   ↑   ↑               ↑   ↑
   |   |               |   |
rend() |         rbegin()  end()
       |                   rbegin().base()
     begin()
     rend().base()

반복자가 요소 사이의 위치를 ​​표시하는 시각화에서 관계는 더 간단합니다.

  +---+---+---+---+---+
  | 1 | 2 | 3 | 4 | 5 |
  +---+---+---+---+---+
  ↑                   ↑
  |                   |
  |                 end()
  |                 rbegin()
begin()             rbegin().base()
rend()
rend().base()

벡터 반복자

begin 은 시퀀스 컨테이너의 첫 번째 요소에 대한 iterator 를 반환합니다.

endend 요소를지나 첫 번째 요소에 iterator 를 반환합니다.

벡터 객체가 const 이면 beginend 모두 const_iterator 반환합니다. 벡터가 const 가 아닐지라도 const_iterator 를 반환하기를 cbegincend 사용할 수 있습니다.

예:

#include <vector>
#include <iostream>

int main() {
    std::vector<int> v = { 1, 2, 3, 4, 5 };  //intialize vector using an initializer_list

    for (std::vector<int>::iterator it = v.begin(); it != v.end(); ++it) {
        std::cout << *it << " ";
    }

    return 0;
}

산출:

1 2 3 4 5

지도 반복자

컨테이너의 첫 번째 요소에 대한 반복자입니다.

맵 객체가 const로 정규화 된 const_iterator 함수는 const_iterator 반환합니다. 그렇지 않으면 iterator 반환 iterator .

// Create a map and insert some values
std::map<char,int> mymap;
mymap['b'] = 100;
mymap['a'] = 200;
mymap['c'] = 300;

// Iterate over all tuples
for (std::map<char,int>::iterator it = mymap.begin(); it != mymap.end(); ++it)
    std::cout << it->first << " => " << it->second << '\n';

산출:

a => 200
b => 100
c => 300

스트림 반복자

스트림 이터레이터는 시퀀스를 읽거나 컨테이너에서 형식이 지정된 데이터를 인쇄 할 때 유용합니다.

// Data stream. Any number of various whitespace characters will be OK.
std::istringstream istr("1\t 2     3 4");
std::vector<int> v;

// Constructing stream iterators and copying data from stream into vector.
std::copy(
    // Iterator which will read stream data as integers.
    std::istream_iterator<int>(istr),
    // Default constructor produces end-of-stream iterator.
    std::istream_iterator<int>(),
    std::back_inserter(v));

// Print vector contents.
std::copy(v.begin(), v.end(),
    //Will print values to standard output as integers delimeted by " -- ".
    std::ostream_iterator<int>(std::cout, " -- "));

예제 프로그램은 표준 출력에 1 -- 2 -- 3 -- 4 -- 인쇄합니다.

자체 생성자 지원 반복자 작성

다른 언어의 일반적인 패턴은 객체의 "스트림"을 생성하고 루프 코드를 사용하여 루프를 돌릴 수있는 기능을 갖추고 있습니다.

우리는 이것을 C ++로 모델링 할 수있다.

template<class T>
struct generator_iterator {
  using difference_type=std::ptrdiff_t;
  using value_type=T;
  using pointer=T*;
  using reference=T;
  using iterator_category=std::input_iterator_tag;
  std::optional<T> state;
  std::function< std::optional<T>() > operation;
  // we store the current element in "state" if we have one:
  T operator*() const {
    return *state;
  }
  // to advance, we invoke our operation.  If it returns a nullopt
  // we have reached the end:
  generator_iterator& operator++() {
    state = operation();
    return *this;        
  }
  generator_iterator operator++(int) {
    auto r = *this;
    ++(*this);
    return r;
  }
  // generator iterators are only equal if they are both in the "end" state:
  friend bool operator==( generator_iterator const& lhs, generator_iterator const& rhs ) {
    if (!lhs.state && !rhs.state) return true;
    return false;
  }
  friend bool operator!=( generator_iterator const& lhs, generator_iterator const& rhs ) {
    return !(lhs==rhs);
  }
  // We implicitly construct from a std::function with the right signature:
  generator_iterator( std::function< std::optional<T>() > f ):operation(std::move(f))
  {
    if (operation)
      state = operation();
  }
  // default all special member functions:
  generator_iterator( generator_iterator && ) =default;
  generator_iterator( generator_iterator const& ) =default;
  generator_iterator& operator=( generator_iterator && ) =default;
  generator_iterator& operator=( generator_iterator const& ) =default;
  generator_iterator() =default;
};

라이브 예제 .

생성 된 요소를 일찍 저장하여 우리가 이미 끝에 있는지 쉽게 알 수 있습니다.

종단 생성자 iterator의 함수는 사용되지 않으므로 std::function 한 번만 복사하여 생성자 반복자를 만들 수 있습니다. 기본 생성자 생성자 iterator는 그 자체와 다른 모든 end-generator-iterator와 비교합니다.



Modified text is an extract of the original Stack Overflow Documentation
아래 라이선스 CC BY-SA 3.0
와 제휴하지 않음 Stack Overflow