1 / 276

Standard C++ Library

Standard C++ Library. 목 차 (1/2). Introduction 반복자 (Iterator) 함수 (function) 와 조건자 (predicate) 컨테이너 (container) 클래스 vector 와 vector<bool> list deque set, multiset, bitset. 목 차 (2/2). map 과 multimap stack 과 queue priority_queue string Generic 알고리즘 예외 처리 (exception handling)

cherie
Download Presentation

Standard C++ Library

An Image/Link below is provided (as is) to download presentation Download Policy: Content on the Website is provided to you AS IS for your information and personal use and may not be sold / licensed / shared on other websites without getting consent from its author. Content is provided to you AS IS for your information and personal use only. Download presentation by click this link. While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server. During download, if you can't get a presentation, the file might be deleted by the publisher.

E N D

Presentation Transcript


  1. Standard C++ Library

  2. 목 차(1/2) • Introduction • 반복자(Iterator) • 함수(function)와 조건자(predicate) • 컨테이너(container) 클래스 • vector와 vector<bool> • list • deque • set, multiset, bitset

  3. 목 차(2/2) • map과 multimap • stack과 queue • priority_queue • string • Generic 알고리즘 • 예외 처리(exception handling) • complex

  4. Introduction

  5. 목 차 • 표준 C++ 라이브러리(standard C++ library) • 다른 라이브러리와의 차이점 • 비 객체지향설계의 장단점 • STL의 구조 • STL 맛보기

  6. 표준 C++ 라이브러리(1/2) • 국제 표준 기구(International Standards Organization, ISO)와 미국 국가 표준 기관(American National Standards Institute, ANSI)은 C++ 프로그래밍 언어의 표준화 작업을 마침(표준 번호: ISO/IEC 14882) • 이 표준화 과정에서 가장 중요한 부분의 하나가 바로 「표준 C++ 라이브러리(standard C++ library)」이며, 이 라이브러리는 많은 양의 클래스와 함수들을 제공함

  7. 표준 C++ 라이브러리(2/2) • 표준 C++ 라이브러리는 다음을 포함하고 있음 • 많은 양의 데이터 구조와 알고리즘. 특히 이 부분만 따로 「표준 템플릿 라이브러리(standard template library, STL)」라고 부른다. • 입출력 스트림 • locale 기능 • string템플릿 클래스 • complex템플릿 클래스 • numeric_limits템플릿 클래스 • 메모리 관리 기능 • Language support 기능 • 예외 처리(exception handling) 기능 • 수치 배열용으로 최적화된 valarray클래스

  8. 다른 라이브러리와의 차이점 • STL의 구조와 설계는 대부분의 다른 C++ 라이브러리와는 거의 모든 면에서 완전히 다르다. • STL은 캡슐화(encapsulation)를 피하고 있고, 상속(inheritance)을 거의 사용하고 있지 않다 • STL의 설계자는 객체지향 방법을 피했으며, 공통된 데이터 구조를 사용하여 수행하는 작업들을 데이터구조의 표현과 분리하였음 • STL을 '알고리즘의 집합'과 이들 알고리즘을 사용하여 다루는 '데이터 구조들의 집합'으로 보는 것이 적절함

  9. 비 객체지향설계의 장단점(1/2) • 장점 • 소스코드 크기의 축소 • STL에는 약 50여 개의 다양한 알고리즘과 10여 개의 주요 데이터 구조들이 서로 분리되어 있어서, 소스코드의 크기를 줄일 수 있음 • 만약 이렇게 분리를 하지 않는다면, 서로 다른 데이터 구조 각각에 대해 모든 알고리즘들을 또 구현해야 하며, 결국 수백 개의 멤버함수를 필요로 하게 됨 • 유연성 • STL의 알고리즘들이 기존의 C++ 포인터와 배열에도 사용될 수 있음

  10. 비 객체지향설계의 장단점(2/2) • 단점 • 반복자(iterator): 불일치(mismatch)와 무효화(invalidation) • 반복자들이 같은 컨테이너로부터 온 것인지를 증명하기가 불가능 함. 한 컨테이너의 시작 반복자를 다른 컨테이너의 끝 반복자와 같이 사용하게 되면 어떤 일이 일어나게 될 지 장담할 수 없게 됨 • 반복자는 자신과 연관된 컨테이너에 대해 삽입이나 반복을 수행한 뒤에, 무효화 될 수도 있음. 이렇게 무효화된 반복자를 검사도 않고 사용하게 되면 예상치 못한 결과를 초래할 수 있음

  11. STL의 구조 • 라이브러리를 이와 같은 구조로 만든다면 소프트웨어 설계 작업량을 상당히 줄일 수 있음 • 라이브러리가 제공하는 요소들을 사용자가 만든 요소들과 같이 사용할 수 있음

  12. STL 맛보기(1/3) #include <stdlib.h> #include <iostream.h> // qsort()의 인자로 쓰일 비교함수 inline int cmp(const void *a, const void *b) { int aa = *(int *)a; int bb = *(int *)b; return (aa < bb) ? -1 : (aa > bb) ? 1 : 0; } int main() { const int size = 1e5; int array[size]; // 100,000개의 정수로 이루어진 배열 // 입력 int n = 0; while (cin >> array[n]) n++; n--; // 정렬 qsort(array, n, sizeof(int), cmp); // 출력 for (int i = 0; i < n; i++) cout << array[i] << endl; } 프로그램 1: STL을 전혀 사용하지 않음

  13. STL 맛보기(2/3) #include <iostream> #include <vector> #include <algorithm> using namespace std; int main() { vector<int> v; // 입력 int input; while (cin >> input) v.push_back(input); // 정렬 sort(v.begin(), v.end()); // 출력 for (int i = 0; i < v.size(); i++) cout << v[i] << endl; } 프로그램 2: 컨테이너, 반복자, 알고리즘

  14. STL 맛보기(3/3) #include <iostream> #include <vector> #include <algorithm> using namespace std; int main() { vector<int> v; istream_iterator<int> start(cin), end; back_insert_iterator<vector<int>> dest(v); // 입력 copy(start, end, dest); // 정렬 sort(v.begin(), v.end()); // 출력 copy(v.begin(), v.end(), ostream_iterator<int>(cout, "\n")); } 프로그램 3: 반복자 어댑터

  15. Iterator(반복자)

  16. 목 차 • 반복자에 대한 소개 • 반복자의 종류 • 스트림 반복자 • 삽입 반복자 • 반복자 연산

  17. 반복자에 대한 소개(1/5) • 반복자(Iterator) • 포인터와 상당히 비슷하며, 컨테이너에 저장되어 있는 원소들을 참조할 때 사용함 • 알고리즘마다 각기 다른 방식으로 컨테이너를 훑어가기 때문에, 반복자에도 여러 가지 종류가 있게 됨 • 컨테이너와 알고리즘 사이에 매개체 역할을 함 • 프로그래머는 표준 라이브러리에서 제공하는 컨테이너들에 알맞은 반복자를 생성할 수 있음

  18. 반복자에 대한 소개(2/5) • 구간(Range) • 구간(range)이란 컨테이너에 담긴 값들의 시퀀스를 나타냄 • 구간은 반복자 한 쌍으로 나타내며, 이 반복자들이 각각 시퀀스의 시작과 끝을 가리킴 • 이 두개의 반복자는 같은 컨테이너로부터 생성된 것이어야 하며 두번째 반복자는 첫번째 반복자 이후에 와야 함 • 널 포인터를 참조하는 것이 논리적인 에러를 유발할 수 있듯이, 어떤 값도 가리키지 않는 반복자를 참조하는 것은 에러를 유발함

  19. 반복자에 대한 소개(3/5) • 구간(Range) • 두번째 반복자가 가리키는 원소는 구간의 일부로 인정되지 않음(past-the-end 원소라고 하며, 구간의 마지막 원소 다음에 위치한 원소를 지칭) • 범위의 끝을 나타내는 반복자(두번째 반복자)는 아무 의미가 없는 것을 가리키고 있으므로 참조하는 것은 피해야 함 • end()는 끝이 아니다? • end()가 가리키는 것은 컨테이너의 맨 마지막 원소가 아니라는 점에 항상 주의 • end()가 가리키고 있는 것은 맨 마지막 원소의 바로 다음번 원소이다 (past-the-end 반복자)

  20. 반복자에 대한 소개(4/5) • end()는 끝이 아니다? • 아무 원소를 가지고 있지 않은 컨테이너의 begin()과 end()는 같아짐 • 기존의 포인터와 같이, 반복자를 수정하는 기본적인 연산은 증가 연산자(++)이다 • 반복자 i에 유한번의 증가 연산자를 적용하여, 반복자 i가 j와 같아질 수 있다면, 반복자 j는 반복자 i로부터 도달가능 (Reachable)하다고 함 bool empty(const STL_Container& container) { return container.begin() == container.end(); }

  21. 반복자에 대한 소개(5/5) • 반복자 구간(iterator range) • 반복자 2개를 사용하여 컨테이너의 특정 구간에 속한 원소들을 나타내고자 한다면, 두번째 반복자가 첫번째 반복자로부터 도달가능(reachable)해야 함 • 그렇지 않으면, 에러가 발생한다. 이는 알고리즘이나 컨테이너 내부에서 검사하지 않기 때문에, 프로그래머가 책임져야 할 부분이다

  22. 반복자의 종류(1/3) • 입력 반복자(input iterator) • 읽기만 가능, 순방향 이동 • 출력 반복자(output iterator) • 쓰기만 가능, 순방향 이동 • 순방향 반복자(forward iterator) • 읽기/쓰기 모두 가능, 순방향 이동 • 양방향 반복자(bidirectional iterator) • 읽기/쓰기 모두 가능, 순방향/역방향 이동 • 임의 접근 반복자(random access iterator) • 읽기/쓰기 모두 가능, 임의접근

  23. 반복자의 종류(2/3) • 반복자는 계층적으로 분류된다 • 순방향 반복자는 입력 반복자와 출력 반복자를 필요로 하는 곳이라면 언제든지 사용될 수 있음 • 양방향 반복자는 순방향 반복자가 올 수 있는 자리에 올 수 있음 • 양방향 반복자를 필요로 하는 상황에서는 임의접근 반복자를 사용할 수 있음

  24. 반복자 종류 생성되는 방식 읽기 접근 쓰기 증감 비교 입력 반복자(input iterator) istream_iterator =*p -> ++ == != 출력 반복자(output iterator) ostream_iteratorinserterfront_inserterback_inserter *p= ++ 순방향 반복자(forward iterator) =*p -> *p= ++ == != 양방향 반복자(bidirectional iterator) list set과 multiset map과 multimap =*p -> *p= ++ -- == != 임의접근 반복자(random access iterator) 일반 포인터 vector deque =*p ->[] *p= ++ --+ - += -= == !=< > <= >= 반복자의 종류(3/3)

  25. 입력 반복자(input iterator)(1/3) • 가장 단순한 형태의 반복자이다 • 위 알고리즘은 입력 반복자가 갖춰야 할 세가지 요구사항을 설명 • 반복자는 다른 반복자와의 상등여부를 비교할 수 있어야 함 • 반복자는 * 연산자를 사용하여 반복자가 가리키는 값을 얻을 수 있어야 함 • 반복자는 ++ 연산자를 사용하여 다음 원소를 가리킬 수 있도록 증가될 수 있어야 함 template <class InputIterator, class T> InputIterator find (InputIterator first, InputIterator last, const T& value) { while (first != last && *first != value) ++first; return first; }

  26. 입력 반복자(input iterator)(2/3) • 세가지 종류의 입력 반복자가 존재 • 일반 포인터 • 일반 포인터를 참조하고 증가시킬 수 있고, 또한 임의의 값을 접근할 수 있으므로 일반 포인터는 입/출력 반복자, 또는 임의접근 반복자로 사용될 수 있음 • 표준 라이브러리의 generic 알고리즘은 표준 라이브러리에서 제공되는 컨테이너뿐만 아니라 C++ 배열에도 사용될 수 있다 int data[100]; ... int *where = find(data, data + 100, 7);

  27. 입력 반복자(input iterator)(3/3) • 세가지 종류의 입력 반복자가 존재 • 컨테이너 반복자(container iterator) • 표준 라이브러리가 제공하는 다양한 컨테이너로부터 생성된 반복자들은 모두 입력 반복자가 갖추어야 할 조건을 만족 (begin(), end()) • 위 예제에서 iterator 대신, const_iterator를 사용하여 선언하면, 그 반복자는 상수 반복자가 된다 • 입력 스트림 반복자(input stream iterator) • 표준 라이브러리는 입력 반복자를 사용하여 입력 스트림에 대해 작업을 할 수 있는 방법을 제공 • istream_iterator클래스를 통해 제공 vector<int>::iterator where = find(v.begin(), v.end(), 7);

  28. 출력 반복자(output iterator)(1/2) • 시퀀스에 값을 대입하는데 사용될 수 있지만, 값을 접근하는데는 사용될 수 없다 • 병렬 시퀀스 • 많은 수의 알고리즘들이 두개의 병렬 시퀀스를 다룸 • 두 번째 시퀀스는 반복자 한쌍 대신 시작 반복자 한개만을 사용하여 표시 • 두 번째 시퀀스가 적어도 첫번째 시퀀스만큼의 원소 갯수를 가지고 있다고 가정 template <class InputIterator, class OutputIterator> OutputIterator copy(InputIterator first, InputIterator last, OutputIterator result) { while (first != last) *result++ = *first++; return result; }

  29. 출력 반복자(output iterator)(2/2) • 4가지 종류의 출력 반복자가 존재 • 일반적인 포인터 • 표준 라이브러리의 컨테이너가 생성한 모든 반복자 • 출력 스트림 반복자 • 삽입 반복자 • 컨테이너에 대한 삽입연산 int data[100]; vector<int> newdata(100); ... copy (data, data+100, newdata.begin());

  30. 순방향 반복자(forward iterator) • 값을 접근하고 갱신하는 것이 가능 • 일반적인 포인터는 순방향 반복자로 사용될 수 있다 template <class ForwardIterator, class T> void replace (ForwardIterator first, ForwardIterator last, const T& old_value, const T& new_value) { while (first != last) { if (*first == old_value) *first = new_value; ++first; } } replace(aVec.begin(), aVec.end(), 7, 11);

  31. 양방향 반복자(bidirectional iterator) (1/2) • 감소 연산자(--)를 지원하므로, 컨테이너내의 원소들을 순방향이나 역방향으로 이동하는 것이 허용 template <class BidirectionalIterator, class OutputIterator> OutputIterator reverse_copy (BidirectionalIterator first, BidirectionalIterator last, OutputIterator result) { while (first != last) *result++ = *--last; return result; } list<int> aList; .... vector<int> aVec(aList.size()); reverse_copy(aList.begin(), aList.end(), aVec.begin());

  32. 양방향 반복자(bidirectional iterator) (2/2) • 일반적인 포인터는 양방향 반복자로 사용될 수 있다

  33. 임의접근 반복자(random access iterator) (1/2) • 기존의 일반 포인터가 했던 모든 것을 할 수 있다 • 첨자에 의한 접근이 가능 • 다른 반복자와의 차(두 반복자 사이에 존재하는 원소의 수)를 구할 수 있음 • 포인터에 대한 산술연산 가능 • 예: x+10 (x부터 10개 원소 이후의 위치를 나타냄) • 정렬과 이진검색과 같은 알고리즘을 수행하는데 주로 사용

  34. 임의접근 반복자(random access iterator) (2/2) template <class RandomAccessIterator> void mixup(RandomAccessIterator first, RandomAccessIterator last) { while (first < last) { iter_swap(first, first + randomInteger(last - first)); ++first; } } // n보다 작고 0보다 크거나 작은 정수형 난수를 리턴 unsigned int randomInteger(unsigned int n) { return rand() % n; } • 일반 포인터는 임의 접근 반복자로 사용될 수 있음

  35. 역 반복자(reverse iterator) (1/2) • 표준 반복자가 부여 받은 순서와는 반대되는 순서로 값들을 접근한다 • vector나 list에서 역 반복자는 마지막 원소를 맨 먼저 접근하고, 첫번째 원소를 맨 마지막에 접근 • set에서는 가장 큰 원소가 맨 먼저 얻어지고, 가장 작은 원소가 마지막에 접근 • list, set, map 타입은 역 양방향 반복자를 생성하는 멤버 함수를 한 쌍 가지고 제공 • rbegin()과 rend()함수는 해당 컨테이너를 역순으로 훑는 반복자를 생성한다 • 역 반복자에 대한 증가연산은 후퇴를 의미하고, 감소연산은 전진을 의미

  36. 역 반복자(reverse iterator) (2/2) • vector와 deque 타입에서는 역 임의접근 반복자를 생성하는 멤버 함수가 제공 • rbegin()과 rend() • 증가연산,덧셈 연산 역시 후퇴를 의미

  37. 스트림 반복자(stream iterator) • 스트림 반복자는 반복자 연산을 이용하여 입력 또는 출력 스트림을 접근하는데 사용 • 두 가지 종류 • 입력 스트림 반복자 • 출력 스트림 반복자

  38. 입력 스트림 반복자 (1/3) • 반복자 연산을 통해 입력 스트림으로부터의 read 작업을 수행 • istream_iterator객체를 생성하여 수행 • istream_iterator의 생성자로 들어가는 인자는 접근할 스트림을 나타냄 • 입력 스트림 반복자에 대해 증가 연산자(++)가 적용될 때마다 스트림으로부터 새로운 값을 읽어(>> 연산자 사용) 저장함 • 이 값은 참조 연산자(*)를 사용하여 얻을 수 있음 • 생성자에 아무런 인자도 넣지 않고 istream_iterator에 의해 생성된 객체는 입력의 끝을 나타내는 반복자로 사용됨

  39. 입력 스트림 반복자 (2/3) • 입력 스트림 반복자는 입력 반복자이기 때문에, 원소들에 대한 접근만이 가능 • 원소들은 오직 한번 순방향으로만 접근이 가능

  40. 입력스트림 반복자 (3/3) #include <iostream> #include <vector> #include <algorithm> using namespace std; int main() { vector<int> v; istream_iterator<int> intstream(cin), eof; while(intstream != eof) v.push_back(*intstream++); for(int i=0;i < v.size();i++) cout << v[i] << endl; } #include <iostream> #include <vector> #include <algorithm> using namespace std; int main() { vector<int> v; istream_iterator<int> intstream(cin), eof; back_insert_iterator<vector<int> > dest(v); copy(intstream, eof, dest); for(int i=0;i < v.size();i++) cout << v[i] << endl; }

  41. 출력 스트림 반복자 (1/2) • 반복자 연산이 수행될 때 출력 스트림으로 write 작업이 수행됨 • 값들이 반복자로 대입될 때마다, 내부적으로 << 연산자를 통해 해당 출력 스트림으로 출력됨 • 출력 스트림 반복자를 생성하기 위해서는 생성자의 인자로 출력 스트림을 지정해야 함 • 생성자의 생략 가능한 두번째 인자는 출력되는 값들 사이의 분리자로 사용될 문자열을 나타냄

  42. 출력 스트림 반복자 (2/2) #include <iostream> #include <vector> #include <algorithm> using namespace std; int main() { vector<int> v; istream_iterator<int> intstream(cin), eof; ostream_iterator<int> outstream(cout,"\n"); while(intstream != eof) v.push_back(*intstream++); for(int i=0;i < v.size();i++) *outstream++ = v[i]; } #include <iostream> #include <vector> #include <algorithm> using namespace std; int main() { vector<int> v; istream_iterator<int> intstream(cin), eof; ostream_iterator<int> outstream(cout,"\n"); back_insert_iterator<vector<int> > dest(v); copy(intstream, eof, dest); copy(v.begin(),v.end(),outstream); }

  43. 삽입 반복자 (1/4) • 출력 반복자에 의해 참조되는 값에 대입을 하면, 그 위치의 내용을 덮어쓰게 됨 vector<int> a(10); … list<int> c; ... copy(a.begin(), a.end(), c.begin());

  44. 삽입 반복자 (2/4) • 삽입 반복자라고 불리는 일종의 어댑터를 사용하여 copy()와 같은 알고리즘을 사용할 때 원소들을 덮어쓰지 않고, 해당 컨테이너로 삽입할 수 있음 vector<int> a(10); … list<int> d; … copy(a.begin(), a.end(), front_inserter(d)); vector의 원소들을 list에 삽입하는 코드

  45. 삽입 반복자 (3/4) • 3가지 종류의 삽입 반복자가 존재 • front_inserter() • 해당 컨테이너의 앞쪽에 값들을 삽입 • list와 deque 컨테이너 클래스에서 사용 가능 • back_inserter() • 해당 컨테이너의 뒤쪽에 값들을 삽입 • list, deque, set, vector 등의 컨테이너 클래스에서 사용 가능

  46. 삽입 반복자 (4/4) • 3가지 종류의 삽입 반복자가 존재 • inserter() • 가장 일반적인 형태의 삽입 반복자 • Inserter의 생성자는 두개의 인자를 취함(컨테이너, 컨테이너내의 반복자) • 원소들을 컨테이너의 특정 위치로 복사(list의 경우, 원소들이 지정된 위치의 바로 앞쪽에 복사됨) • list, deque, set, vector, map, stack 등의 모든 컨테이너 클래스에서 사용 가능

  47. #include <iostream> #include <vector> #include <list> #include <algorithm> using namespace std; int main() { int threeToOne[] = {3, 2, 1}; int fourToSix[] = {4, 5, 6}; int sevenToNine[] = {7, 8, 9}; list<int> aList; // first insert into the front // note that each value becomes new front copy(threeToOne, threeToOne+3, front_inserter(aList)); // then insert into the back copy(sevenToNine, sevenToNine+3, back_inserter(aList)); // find the seven, and insert into middle list<int>::iterator seven = find(aList.begin(), aList.end(), 7); copy(fourToSix, fourToSix+3, inserter(aList, seven)); // copy result to output copy(aList.begin(), aList.end(), ostream_iterator<int>(cout, " ")); cout << endl; } 실행 결과: 1 2 3 4 5 6 7 8 9

  48. Traits class(1/2) template <class Iterator> struct iterator_traits { typedef typename Iterator::difference_type difference_type; typedef typename Iterator::value_type value_type; typedef typename Iterator::pointer pointer; typedef typename Iterator::reference reference; typedef typename Iterator::iterator_category iterator_category; };

  49. Traits class(2/2) template <class T> struct iterator_traits<T*> { typedef ptrdiff_t difference_type; // ptrdiff_t : stddef.h typedef T value_type; typedef T* pointer; typedef T& reference; typedef random_access_iterator_tag iterator_category; };

  50. Tag classes struct input_iterator_tag {}; struct output_iterator_tag {}; struct forward_iterator_tag : public input_iterator_tag {}; struct bidirectoional_iterator_tag : public forward_iterator_tag {}; struct random_access_iterator_tag : public bidirectional_iterator_tag {};

More Related