C ++ Iterator, Iterator 기본 클래스가없는 이유


11

저는 시험을 배우고 있으며 답변을 드리려고 애쓰는 질문이 있습니다.

다른 모든 반복자가 상속 한 반복자 기본 클래스가없는 이유는 무엇입니까?

내 선생님은 cpp reference " http://prntscr.com/mgj542 " 에서 계층 구조를 참조 하고 있는데 왜 다른 이유를 제공해야합니까?

반복자가 무엇인지 (정렬 한) 컨테이너에서 작업하는 데 사용된다는 것을 알고 있습니다. 가능한 다른 기본 데이터 구조로 인해 내가 이해하는 것에서, 예를 들어 연결된 목록에 무작위로 액세스 할 수는 없지만 다른 컨테이너는 다른 방법으로 배열을 이동해야하기 때문에 다른 컨테이너에는 다른 반복자가 있습니다.

컨테이너에 따라 특수 템플릿 일 것입니다.


2
귀하의 연구를 공유하면 모든 사람을 도울 수 있습니다. 당신이 무엇을 시도했고 왜 그것이 당신의 요구를 충족시키지 못했는지 알려주십시오. 이것은 당신이 시간을내어 자신을 돕고, 명백한 답변을 되풀이하는 것을 막아 주며, 무엇보다도보다 구체적이고 적절한 답변을 얻는 데 도움이됩니다. 또한 물어
gnat

5
" 왜 반복자의 기본 클래스는 다른 모든 반복자는 상속 존재하지 않습니다? "음 ... 왜 해야 하나가?
Nicol Bolas

답변:


14

모든 반복자가 단일 반복자 기본 클래스에서 상속 할 필요가없는 이유에 대한 답변을 이미 받았습니다. 그래도 꽤 조금 더 있습니다. C ++의 목표 중 하나는 런타임 비용이없는 추상화입니다.

반복자가 공통 기본 클래스에서 상속받은 모든 작업에 의해 작동하고 기본 클래스의 가상 함수를 사용하여 인터페이스를 정의하고 파생 클래스가 해당 가상 함수의 구현을 제공하여 상당한 런타임을 추가 할 수있는 경우 관련된 작업에 대한 오버 헤드.

예를 들어 상속과 가상 함수를 사용하는 간단한 반복자 계층 구조를 생각해 봅시다.

template <class T>
class iterator_base { 
public:
    virtual T &operator*() = 0;
    virtual iterator_base &operator++() = 0;
    virtual bool operator==(iterator_base const &other) { return pos == other.pos; }
    virtual bool operator!=(iterator_base const &other) { return pos != other.pos; }
    iterator_base(T *pos) : pos(pos) {}
protected:
    T *pos;
};

template <class T>
class array_iterator : public iterator_base<T> {
public: 
    virtual T &operator*() override { return *pos; }
    virtual array_iterator &operator++() override { ++pos; return *this; }
    array_iterator(T *pos) : iterator_base(pos) {}
};

그런 다음 빠른 테스트를 해보자.

int main() { 
    char input[] = "asdfasdfasdfasdfasdfasdfasdfadsfasdqwerqwerqwerqrwertytyuiyuoiiuoThis is a stringy to search for something";
    using namespace std::chrono;

    auto start1 = high_resolution_clock::now();
    auto pos = std::find(std::begin(input), std::end(input), 'g');
    auto stop1 = high_resolution_clock::now();

    std::cout << *++pos << "\n";

    auto start2 = high_resolution_clock::now();
    auto pos2 = std::find(array_iterator(input), array_iterator(input+sizeof(input)), 'g');
    auto stop2 = high_resolution_clock::now();

    std::cout << *++pos2 << "\n";

    std::cout << "time1: " << duration_cast<nanoseconds>(stop1 - start1).count() << "ns\n";
    std::cout << "time2: " << duration_cast<nanoseconds>(stop2 - start2).count() << "ns\n";
}

[참고 : 컴파일러에 따라 컴파일러가 반복자를 허용하도록 iterator_category, difference_type, 참조 등을 정의하는 등 약간의 추가 작업이 필요할 수 있습니다.]

그리고 출력은 다음과 같습니다

y
y
time1: 1833ns
time2: 2933ns

[물론, 코드를 실행하면 결과가 정확하게 일치하지 않습니다.]

따라서이 간단한 경우 (약 80 증분 및 비교 만 수행)에도 간단한 선형 검색에 약 60 %의 오버 헤드가 추가되었습니다. 특히 반복자가 원래 C ++에 추가되었을 때, 상당수의 사람들이 오버 헤드가 많은 디자인을 받아들이지 않았을 것입니다. 아마도 표준화되지 않았을 것입니다. 실제로 사용하더라도 사실상 아무도 사용하지 않을 것입니다.


7

차이점은 무엇 인가와 어떻게 행동 하는가입니다.

많은 언어들이이 두 언어를 서로 혼동 시키려고하지만, 그것들은 상당히 뚜렷합니다.

어떻게, 어떻게, 어떻게 ...

모든 것이 상속되면 object다음과 같은 이점이 발생합니다. 객체의 모든 변수는 어떤 가치도 가질 수 있습니다. 그러나 문질러도 즉, 모든 해야한다 (행동 어떻게 ' 등) object(같은 해 봐 무엇을 )을을 object.

그러나:

  • 객체에 평등에 대한 의미있는 정의가 없으면 어떻게됩니까?
  • 의미있는 해시가 없으면 어떻게됩니까?
  • 객체를 복제 할 수 없지만 객체를 복제 할 수 있으면 어떻게됩니까?

어느 object유형은 본질적으로 쓸모가된다 - 때문에 객체에 가능한 인스턴스 모두에서 어떤 공통점을 제공합니다. 또는 다수의 문제를 제외하고는 거의 보편적 인 행동 을 object나타내는 것으로 추정되는 보편적 인 재산에 대한 부러진 / 발굽이 있거나 터무니없는 정의를 가진 물체가 존재할 것이다 .

어떻게 묶이지 않는지

또는 WhatHow를 별도로 유지할 수 있습니다 . 그런 다음 몇 가지 다른 유형 (모두 공통적 인 내용은 없음 )은 모두 공동 작업자 방식과 동일한 방식으로 작동 할 수 있습니다 . 이런 의미에서의 개념은 무엇Iterator대한 것이 아니라 방법 이다. 구체적으로 어떻게 일이 당신을 상호 작용 할 아직 모를 때 무엇을 당신이와 상호 작용한다.

Java (및 유사)는 인터페이스를 사용하여 이에 대한 접근을 허용합니다. 이와 관련하여 인터페이스는 통신 수단, 그리고 암시 적으로 따르는 통신 및 행동의 프로토콜을 설명합니다. 어떤 어떤 특정의 수 자체를 선언하는 방법 은 프로토콜에 의해 설명 된 관련 통신 및 활동을 지원, 국가. 이것은 어떤 협력자가에 의존 할 수 있습니다 방법 과를 정확하게 지정하여 속도가 느려하지 무엇 의 사용할 수 있습니다.

C ++ (및 유사)는 오리 타이핑을 통해 이에 대한 접근을 허용합니다. 템플릿은 공동 작업 유형이 특정 컴파일 컨텍스트 내에서 특정 방식으로 객체와 상호 작용할 수있는 동작을 따르는 것으로 선언하는 경우 상관하지 않습니다. 이를 통해 C ++ 포인터와 특정 연산자를 재정의하는 Object를 동일한 코드에서 사용할 수 있습니다. 그들은 체크리스트를 충족시키기 때문에 동등한 것으로 간주됩니다.

  • * a, a->, ++ a 및 a ++-> 입력 / 전달 반복자를 지원합니다.
  • * a, a->, ++ a, a ++, --a 및 a---> 양방향 반복기를 지원합니다.

기본 유형은 심지어 어떤 수, 반복하는에게 용기를 할 필요가 없습니다 . 또한 일부 공동 작업자는보다 포괄적 인 기능을 수행하고 함수 만 필요하다고 생각하고 a++반복자는이를 만족시킬 수 있으므로 포인터도 정수도 가능하므로 모든 객체를 구현할 수 operator++있습니다.

과소 사양

두 가지 접근 방식의 문제점은 사양 이하입니다.

인터페이스를 사용하려면 객체가 지정된 동작을 지원하도록 선언해야합니다. 이는 또한 제작자가 처음부터 인터페이스를 부여해야 함을 의미합니다. 이것은 그들이 선언하지 않았기 때문에 자르지 않는 것이 무엇인지를 유발 합니다. 어느 것을 그것은 또한 수단 어떤 공통 조상을 가지고, 인터페이스가 나타내는 방법을 . 이것은의 초기 문제로 돌아갑니다 object. 이로 인해 공동 작업자는 요구 사항을 과도하게 지정하는 동시에 선언 부족으로 인해 일부 개체를 사용할 수 없게되거나 예상되는 동작이 제대로 정의되지 않아 숨겨진 문제가 발생할 수 있습니다.

템플릿을 사용하려면 공동 작업자가 완전히 알려지지 않은 What으로 작업해야 하며 상호 작용을 통해 How를 정의합니다 . 어느 정도까지는 컴파일 오류를 피하면서 통신 기본 요소 (기능 / 필드 / 등)에 대한 내용 을 분석 하거나 최소한 주어진 내용방법에 대한 요구 사항과 일치하지 않는 방법을 지적 해야하기 때문에 공동 작업자를 작성하기가 더 어려워 집니다 . 이것은 주어진에서 최소한으로 요구하는 협력자 수 있습니다 무엇을 가장 광범위한 수 있도록 무엇을 '사용들. 불행히도 이것은 기술적으로 주어진 통신 기본 요소를 제공하는 객체의 무의미한 사용을 허용한다는 단점이 있습니다.어떻게 ,하지만 나쁜 모든 일이 발생할 수 암시 프로토콜을 따르지 않습니다.

반복자

이 경우에는 IteratorA는 어떻게 이 상호 작용에 대한 설명은 속기이다. 해당 설명과 일치하는 것은 정의상으로 Iterator입니다. 알고 어떻게 우리가 일반적으로 알고리즘을 작성하고 '의 짧은 목록을 가질 수 있습니다 어떻게 특정 주어 s의'를 무엇 '필요가 알고리즘 작동 할 수 있도록하기 위해 제공 될 수있다. 이 목록은 함수 / 속성 / 기타이며, 구현시 알고리즘이 처리 하는 특정 내용 을 고려합니다 .


6

C ++은하지 않기 때문에 필요 다형성을 할 수있는 (추상) 기본 클래스를 가질 수 있습니다. 그것은이 구조 하위 유형 뿐만 아니라 주격 하위 유형을 .

반복자의 특정 경우에 혼란스럽게도, 이전 표준 std::iterator은 (대략)

template <class Category, class T, class Distance = std::ptrdiff_t, class Pointer = T*, class Reference = T&>
struct iterator {
    using iterator_category = Category;
    using value_type = T;
    using difference_type = Distance;
    using pointer = Pointer;
    using reference = Reference;
}

즉, 필요한 멤버 유형의 공급자 일뿐입니다. 런타임 동작이 없었 으며 C ++ 17에서 더 이상 사용되지 않습니다.

클래스 템플릿은 클래스가 아니므로 각 인스턴스화는 다른 인스턴스와 독립적입니다.



5

반복자가 클래스의 인스턴스 일 필요는 없기 때문입니다. 예를 들어, 포인터는 많은 경우에 완벽하게 좋은 반복자이며, 기본 요소이기 때문에 어떤 것도 상속 할 수 없습니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.