Transcript STL

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)
complex
Introduction
목차





표준 C++ 라이브러리(standard C++ library)
다른 라이브러리와의 차이점
비 객체지향설계의 장단점
STL의 구조
STL 맛보기
표준 C++ 라이브러리(1/2)


국제 표준 기구(International Standards
Organization, ISO)와 미국 국가 표준 기관
(American National Standards Institute,
ANSI)은 C++ 프로그래밍 언어의 표준화 작업
을 마침(표준 번호: ISO/IEC 14882)
이 표준화 과정에서 가장 중요한 부분의 하나
가 바로 「표준 C++ 라이브러리(standard
C++ library)」이며, 이 라이브러리는 많은 양
의 클래스와 함수들을 제공함
표준 C++ 라이브러리(2/2)

표준 C++ 라이브러리는 다음을 포함하고 있음










많은 양의 데이터 구조와 알고리즘. 특히 이 부분만 따로 「표
준 템플릿 라이브러리(standard template library, STL)」라고
부른다.
입출력 스트림
locale 기능
string 템플릿 클래스
complex 템플릿 클래스
numeric_limits 템플릿 클래스
메모리 관리 기능
Language support 기능
예외 처리(exception handling) 기능
수치 배열용으로 최적화된 valarray 클래스
다른 라이브러리와의 차이점

STL의 구조와 설계는 대부분의 다른 C++ 라이브러리
와는 거의 모든 면에서 완전히 다르다.


STL은 캡슐화(encapsulation)를 피하고 있고, 상속
(inheritance)을 거의 사용하고 있지 않다
STL의 설계자는 객체지향 방법을 피했으며, 공통된
데이터 구조를 사용하여 수행하는 작업들을 데이터구
조의 표현과 분리하였음

STL을 '알고리즘의 집합'과 이들 알고리즘을 사용하여 다루
는 '데이터 구조들의 집합'으로 보는 것이 적절함
비 객체지향설계의 장단점(1/2)

장점

소스코드 크기의 축소



STL에는 약 50여 개의 다양한 알고리즘과 10여 개의 주
요 데이터 구조들이 서로 분리되어 있어서, 소스코드의
크기를 줄일 수 있음
만약 이렇게 분리를 하지 않는다면, 서로 다른 데이터 구
조 각각에 대해 모든 알고리즘들을 또 구현해야 하며, 결
국 수백 개의 멤버함수를 필요로 하게 됨
유연성

STL의 알고리즘들이 기존의 C++ 포인터와 배열에도 사
용될 수 있음
비 객체지향설계의 장단점(2/2)

단점

반복자(iterator): 불일치(mismatch)와 무효화
(invalidation)


반복자들이 같은 컨테이너로부터 온 것인지를 증명하기
가 불가능 함. 한 컨테이너의 시작 반복자를 다른 컨테이
너의 끝 반복자와 같이 사용하게 되면 어떤 일이 일어나
게 될 지 장담할 수 없게 됨
반복자는 자신과 연관된 컨테이너에 대해 삽입이나 반복
을 수행한 뒤에, 무효화 될 수도 있음. 이렇게 무효화된
반복자를 검사도 않고 사용하게 되면 예상치 못한 결과를
초래할 수 있음
STL의 구조


라이브러리를 이와 같은 구조로 만든다면 소프트웨어
설계 작업량을 상당히 줄일 수 있음
라이브러리가 제공하는 요소들을 사용자가 만든 요소
들과 같이 사용할 수 있음
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을
전혀 사용하지 않음
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: 컨테이너, 반복
자, 알고리즘
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: 반복자 어댑터
Iterator(반복자)
목차





반복자에 대한 소개
반복자의 종류
스트림 반복자
삽입 반복자
반복자 연산
반복자에 대한 소개(1/5)

반복자(Iterator)




포인터와 상당히 비슷하며, 컨테이너에 저장되어
있는 원소들을 참조할 때 사용함
알고리즘마다 각기 다른 방식으로 컨테이너를 훑
어가기 때문에, 반복자에도 여러 가지 종류가 있
게됨
컨테이너와 알고리즘 사이에 매개체 역할을 함
프로그래머는 표준 라이브러리에서 제공하는 컨테
이너들에 알맞은 반복자를 생성할 수 있음
반복자에 대한 소개(2/5)

구간(Range)




구간(range)이란 컨테이너에 담긴 값들의 시퀀스
를 나타냄
구간은 반복자 한 쌍으로 나타내며, 이 반복자들
이 각각 시퀀스의 시작과 끝을 가리킴
이 두개의 반복자는 같은 컨테이너로부터 생성된
것이어야 하며 두번째 반복자는 첫번째 반복자 이
후에 와야 함
널 포인터를 참조하는 것이 논리적인 에러를 유발
할 수 있듯이, 어떤 값도 가리키지 않는 반복자를
참조하는 것은 에러를 유발함
반복자에 대한 소개(3/5)

구간(Range)



두번째 반복자가 가리키는 원소는 구간의 일부로 인정되지
않음(past-the-end 원소라고 하며, 구간의 마지막 원소 다
음에 위치한 원소를 지칭)
범위의 끝을 나타내는 반복자(두번째 반복자)는 아무 의미가
없는 것을 가리키고 있으므로 참조하는 것은 피해야 함
end()는 끝이 아니다?


end()가 가리키는 것은 컨테이너의 맨 마지막 원소가 아니
라는 점에 항상 주의
end()가 가리키고 있는 것은 맨 마지막 원소의 바로 다음번
원소이다 (past-the-end 반복자)
반복자에 대한 소개(4/5)

end()는 끝이 아니다?

아무 원소를 가지고 있지 않은 컨테이너의 begin()과 end()
는 같아짐
bool empty(const STL_Container& container) {
return container.begin() == container.end();
}


기존의 포인터와 같이, 반복자를 수정하는 기본적인 연산은
증가 연산자(++)이다
반복자 i에 유한번의 증가 연산자를 적용하여, 반복자 i가 j와
같아질 수 있다면, 반복자 j는 반복자 i로부터 도달가능
(Reachable)하다고 함
반복자에 대한 소개(5/5)

반복자 구간(iterator range)


반복자 2개를 사용하여 컨테이너의 특정 구간에
속한 원소들을 나타내고자 한다면, 두번째 반복자
가 첫번째 반복자로부터 도달가능(reachable)해
야함
그렇지 않으면, 에러가 발생한다. 이는 알고리즘
이나 컨테이너 내부에서 검사하지 않기 때문에,
프로그래머가 책임져야 할 부분이다
반복자의 종류(1/3)

입력 반복자(input iterator)


출력 반복자(output iterator)


읽기/쓰기 모두 가능, 순방향 이동
양방향 반복자(bidirectional iterator)


쓰기만 가능, 순방향 이동
순방향 반복자(forward iterator)


읽기만 가능, 순방향 이동
읽기/쓰기 모두 가능, 순방향/역방향 이동
임의 접근 반복자(random access iterator)

읽기/쓰기 모두 가능, 임의접근
반복자의 종류(2/3)

반복자는 계층적으로 분류된다



순방향 반복자는 입력 반복자와 출력 반복자를 필
요로 하는 곳이라면 언제든지 사용될 수 있음
양방향 반복자는 순방향 반복자가 올 수 있는 자리
에 올 수 있음
양방향 반복자를 필요로 하는 상황에서는 임의접
근 반복자를 사용할 수 있음
반복자의 종류(3/3)
반복자 종류
생성되는 방식
읽기
접근
입력 반복자
(input iterator)
istream_iterator
=*p
->
출력 반복자
(output iterator)
ostream_iterator
inserter
front_inserter
back_inserter
순방향 반복자
(forward iterator)
쓰기
증감
비교
++
== !=
*p=
++
=*p
->
*p=
++
== !=
양방향 반복자
(bidirectional iterator)
list
set과 multiset
map과 multimap
=*p
->
*p=
++ --
== !=
임의접근 반복자
(random access iterator)
일반 포인터
vector
deque
=*p
->
[]
*p=
++ -+ += -=
== !=
< >
<= >=
입력 반복자(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;
}

위 알고리즘은 입력 반복자가 갖춰야 할 세가지 요구
사항을 설명



반복자는 다른 반복자와의 상등여부를 비교할 수 있어야 함
반복자는 * 연산자를 사용하여 반복자가 가리키는 값을 얻을
수 있어야 함
반복자는 ++ 연산자를 사용하여 다음 원소를 가리킬 수 있도
록 증가될 수 있어야 함
입력 반복자(input iterator)(2/3)

세가지 종류의 입력 반복자가 존재

일반 포인터


일반 포인터를 참조하고 증가시킬 수 있고, 또한 임의의
값을 접근할 수 있으므로 일반 포인터는 입/출력 반복자,
또는 임의접근 반복자로 사용될 수 있음
표준 라이브러리의 generic 알고리즘은 표준 라이브러
리에서 제공되는 컨테이너뿐만 아니라 C++ 배열에도
사용될 수 있다
int data[100];
...
int *where = find(data, data + 100, 7);
입력 반복자(input iterator)(3/3)

세가지 종류의 입력 반복자가 존재

컨테이너 반복자(container iterator)

표준 라이브러리가 제공하는 다양한 컨테이너로부터 생
성된 반복자들은 모두 입력 반복자가 갖추어야 할 조건을
만족 (begin(), end())
vector<int>::iterator where = find(v.begin(), v.end(), 7);


위 예제에서 iterator 대신, const_iterator를 사용하여 선
언하면, 그 반복자는 상수 반복자가 된다
입력 스트림 반복자(input stream iterator)


표준 라이브러리는 입력 반복자를 사용하여 입력 스트림
에 대해 작업을 할 수 있는 방법을 제공
istream_iterator 클래스를 통해 제공
출력 반복자(output iterator)(1/2)

시퀀스에 값을 대입하는데 사용될 수 있지만, 값을 접
근하는데는 사용될 수 없다
template <class InputIterator, class OutputIterator>
OutputIterator copy(InputIterator first, InputIterator last, OutputIterator result)
{
while (first != last)
*result++ = *first++;
return result;
}

병렬 시퀀스



많은 수의 알고리즘들이 두개의 병렬 시퀀스를 다룸
두 번째 시퀀스는 반복자 한쌍 대신 시작 반복자 한개만을
사용하여 표시
두 번째 시퀀스가 적어도 첫번째 시퀀스만큼의 원소 갯수를
가지고 있다고 가정
출력 반복자(output iterator)(2/2)

4가지 종류의 출력 반복자가 존재

일반적인 포인터
int data[100];
vector<int> newdata(100);
...
copy (data, data+100, newdata.begin());



표준 라이브러리의 컨테이너가 생성한 모든 반복자
출력 스트림 반복자
삽입 반복자

컨테이너에 대한 삽입연산
순방향 반복자(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);

일반적인 포인터는 순방향 반복자로 사용될 수 있다
양방향 반복자
(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());
양방향 반복자
(bidirectional iterator) (2/2)

일반적인 포인터는 양방향 반복자로 사용될
수 있다
임의접근 반복자
(random access iterator) (1/2)

기존의 일반 포인터가 했던 모든 것을 할 수
있다



첨자에 의한 접근이 가능
다른 반복자와의 차(두 반복자 사이에 존재하는 원
소의 수)를 구할 수 있음
포인터에 대한 산술연산 가능


예: x+10 (x부터 10개 원소 이후의 위치를 나타냄)
정렬과 이진검색과 같은 알고리즘을 수행하는
데 주로 사용
임의접근 반복자
(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;
}

일반 포인터는 임의 접근 반복자로 사용될 수 있음
역 반복자(reverse iterator) (1/2)

표준 반복자가 부여 받은 순서와는 반대되는 순서로
값들을 접근한다



vector나 list에서 역 반복자는 마지막 원소를 맨 먼저 접근하
고, 첫번째 원소를 맨 마지막에 접근
set에서는 가장 큰 원소가 맨 먼저 얻어지고, 가장 작은 원소
가 마지막에 접근
list, set, map 타입은 역 양방향 반복자를 생성하는
멤버 함수를 한 쌍 가지고 제공


rbegin()과 rend() 함수는 해당 컨테이너를 역순으로 훑는 반
복자를 생성한다
역 반복자에 대한 증가연산은 후퇴를 의미하고, 감소연산은
전진을 의미
역 반복자(reverse iterator) (2/2)

vector와 deque 타입에서는 역 임의접근 반
복자를 생성하는 멤버 함수가 제공


rbegin()과 rend()
증가연산,덧셈 연산 역시 후퇴를 의미
스트림 반복자(stream iterator)


스트림 반복자는 반복자 연산을 이용하여 입
력 또는 출력 스트림을 접근하는데 사용
두 가지 종류


입력 스트림 반복자
출력 스트림 반복자
입력 스트림 반복자 (1/3)

반복자 연산을 통해 입력 스트림으로부터의 read 작
업을 수행





istream_iterator 객체를 생성하여 수행
istream_iterator의 생성자로 들어가는 인자는 접근할 스트림
을 나타냄
입력 스트림 반복자에 대해 증가 연산자(++)가 적용될 때마
다 스트림으로부터 새로운 값을 읽어(>> 연산자 사용) 저장
함
이 값은 참조 연산자(*)를 사용하여 얻을 수 있음
생성자에 아무런 인자도 넣지 않고 istream_iterator에 의해
생성된 객체는 입력의 끝을 나타내는 반복자로 사용됨
입력 스트림 반복자 (2/3)


입력 스트림 반복자는 입력 반복자이기 때문
에, 원소들에 대한 접근만이 가능
원소들은 오직 한번 순방향으로만 접근이 가
능
입력스트림 반복자 (3/3)
#include <iostream>
#include <vector>
#include <algorithm>
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
using namespace std;
int main()
{
vector<int> v;
istream_iterator<int> intstream(cin), eof;
int main()
{
vector<int> v;
istream_iterator<int> intstream(cin), eof;
back_insert_iterator<vector<int> > dest(v);
while(intstream != eof)
v.push_back(*intstream++);
}
for(int i=0;i < v.size();i++)
cout << v[i] << endl;
copy(intstream, eof, dest);
}
for(int i=0;i < v.size();i++)
cout << v[i] << endl;
출력 스트림 반복자 (1/2)

반복자 연산이 수행될 때 출력 스트림으로
write 작업이 수행됨



값들이 반복자로 대입될 때마다, 내부적으로
<< 연산자를 통해 해당 출력 스트림으로 출력됨
출력 스트림 반복자를 생성하기 위해서는 생성자
의 인자로 출력 스트림을 지정해야 함
생성자의 생략 가능한 두번째 인자는 출력되는 값
들 사이의 분리자로 사용될 문자열을 나타냄
출력 스트림 반복자 (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);
삽입 반복자 (1/4)

출력 반복자에 의해 참조되는 값에 대입을 하
면, 그 위치의 내용을 덮어쓰게 됨
vector<int> a(10);
…
list<int> c;
...
copy(a.begin(), a.end(), c.begin());
삽입 반복자 (2/4)

삽입 반복자라고 불리는 일종의 어댑터를 사
용하여 copy()와 같은 알고리즘을 사용할 때
원소들을 덮어쓰지 않고, 해당 컨테이너로 삽
입할 수 있음
vector<int> a(10);
…
list<int> d;
…
copy(a.begin(), a.end(), front_inserter(d));
vector의 원소들을 list에 삽입하는 코드
삽입 반복자 (3/4)

3가지 종류의 삽입 반복자가 존재

front_inserter()



해당 컨테이너의 앞쪽에 값들을 삽입
list와 deque 컨테이너 클래스에서 사용 가능
back_inserter()


해당 컨테이너의 뒤쪽에 값들을 삽입
list, deque, set, vector 등의 컨테이너 클래스에서 사용
가능
삽입 반복자 (4/4)

3가지 종류의 삽입 반복자가 존재

inserter()




가장 일반적인 형태의 삽입 반복자
Inserter의 생성자는 두개의 인자를 취함(컨테이너, 컨테
이너내의 반복자)
원소들을 컨테이너의 특정 위치로 복사(list의 경우, 원소
들이 지정된 위치의 바로 앞쪽에 복사됨)
list, deque, set, vector, map, stack 등의 모든 컨테이너
클래스에서 사용 가능
#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;
실행 결과:
123456789
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;
};
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;
};
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 {};
반복자 연산(iterator operation) (1/2)

void advance(InputIterator& iter, int& n)




하나의 반복자와 하나의 숫자 값을 인자로 받아서,
반복자를 주어진 값만큼 이동
순방향 반복자의 경우에는 반드시 양수이어야 하
고, 양방향 반복자나 임의접근 반복자의 경우에는
양수나 음수이면 된다
임의접근 반복자에 대해서만 효율적(constant
time)
다른 경우에는 반복자에 대해 증가 연산자(++)나
감소 연산자(--)를 호출하는 루프로 advance()를
구현
반복자 연산(iterator operation) (2/2)

void distance(InputIterator first, IuputIterator
last, Distance& n)




두 반복자간의 거리를 반환
결과값은 레퍼런스로 넘어간 세번째 인자로 반환
first를 last로 옮길 때, ++연산자가 수행되는 총 횟
수를 세번째 인자에 반환
세번째 인자를 통해 넘겨지는 변수가 제대로 초기
화가 되었는지를 항상 확인
함수(function)와 조건자(predicate)
Chiyoung Seo
SNU OOPSLA Lab.
목차




함수(function)
조건자(predicate)
함수 객체(function object)
부정자(negator)와 바인더(binder)
함수(function)

Generic 알고리즘중에는 함수를 인자로 요구
하는 것들이 많음


for_each() 알고리즘은 컨테이너에 담긴 각각의 원
소에 인자로 넘겨받은 함수를 적용
이항 함수(binary function)는 두개의 인자를 취하
는데, 서로 다른 두 시퀀스에서 값들을 하나씩 가
져와 함수에 넘겨줌

transform() 알고리즘은 인자로 이항 함수를 요구
#include <iostream>
#include <vector>
#include <list>
#include <algorithm>
using namespace std;
void printElement(int value)
{
cout << "The list contains " << value << endl;
}
void main()
{
int threeToOne[] = {3, 2, 1};
int fourToSix[] = {4, 5, 6};
int sevenToNine[] = {7, 8, 9};
list<int> aList;
// first insert into the 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));
}
for_each(aList.begin(), aList.end(), printElement);
실행결과:
The list contains 1
The list contains 2
The list contains 3
The list contains 4
The list contains 5
The list contains 6
The list contains 7
The list contains 8
The list contains 9
#include <iostream>
#include <vector>
#include <list>
#include <algorithm>
#include "str.h "
using namespace std;
string stringRepeat(const string& base, int number)
{
string result; // 'result'는 처음에 비어 있다.
while (number--) result += base;
return result;
}
void main() {
list<string> words;
list<int> counts;
...
transform(words.begin(), words.end(), counts.begin(),
words.begin(), stringRepeat);
}
문자열 list (one, two, three)과 정수 list
(3, 2, 3)를 인자로 하여 transform()을
호출 했을 시,
(oneoneone, twotwo, threethreethree)의
문자열 list를 얻게 됨
조건자(predicate)


조건자(predicate)는 (true/false) 또는 정수
값을 리턴하는 함수
generic 알고리즘의 인자로 조건자를 사용 가
능

find_if()


조건자를 만족하는 첫번째 원소를 가리키는 반복자를 리
턴
조건자를 만족하는 원소가 없으면 end-of-range 값을
리턴
#include <iostream>
#include <vector>
#include <list>
#include <algorithm>
using namespace std;
// 'year'가 윤년에 해당하면 참을 리턴한다.
bool isLeapYear(unsigned int year)
{
bool flag = false;
if ((year % 4) == 0) {
// 4년마다 윤년이고,
flag = true;
if ((year % 100) == 0) {
// 그 중 100년마다 윤년이 아니고,
flag = false;
if ((year % 400) == 0) { // 그 중 400년마다 윤년
flag = true;
}
}
}
}
return flag;
void main() {
list<int> aList;
istream_iterator<int> intstream(cin), end;
copy(intstream, end, front_inserter(aList));
list<int>::iterator firstLeap = find_if(aList.begin(), aList.end(), isLeapYear);
cout << *(firstLeap) << endl;
}
함수 객체(function object) (1/2)

괄호 연산자, ()를 멤버함수로 가지는 클래스
의 객체


함수 객체를 함수로 사용하면, 함수가 호출될 때마
다 함수 객체의 괄호 연산자가 호출
generic 함수에 함수 객체를 인자로 넘길 수 있음
#include <iostream>
#include <vector>
#include <list>
#include <algorithm>
#include <iostream>
#include <vector>
#include <list>
#include <algorithm>
using namespace std;
using namespace std;
class biggerThan {
public:
const int testValue;
biggerThan(int x) : testValue(x) { }
class biggerThan {
public:
const int testValue;
biggerThan(int x) : testValue(x) { }
};
bool operator()(int val) { return val > testValue; }
void main() {
biggerThan val(10);
cout << val(12) << endl;
}
함수 객체 사용 예제
};
bool operator()(int val) { return val > testValue; }
void main() {
list<int> aList;
istream_iterator<int> intstream(cin), end;
copy(intstream, end, back_inserter(aList));
}
list<int>::iterator firstBig =
find_if(aList.begin(), aList.end(), biggerThan(12));
cout << *(firstBig) << endl;
list에서 12보다 큰 첫번째 값의 위치를 찾아내는 코드
함수 객체(function object) (2/2)

일반 함수 대신에 함수 객체를 사용하는 가장
큰 이유


새로운 함수를 만들지 않고 표준 라이브러리에서
제공되는 함수 객체를 사용 (재 사용성)
함수 객체로 하여금 자신이 가지고 있는 상태정보
를 접근하고, 갱신 (유연성)
재 사용성 (1/3)

표준 라이브러리는 다양한 종류의 함수 객체
를 제공

#include <functional> 헤더 파일 포함시켜야 함
재 사용성 (2/3)
이름
연산
산술 함수(arithmetic function)
plus
minus
multiplies
divides
modulus
negate
x+y
x-y
x*y
x/y
x%y
-x
비교 함수(comparison function)
equal_to
not_equal_to
greater
less
greater_equal
less_equal
x == y
x != y
x>y
x<y
x >= y
x <= y
논리 함수(logical function)
logical_and
logical_or
logical_not
x && y
x || y
!x
#include <iostream>
#include <list>
#include <algorithm>
#include <functional>
using namespace std;
void main()
{
int oneToThree[] = {1, 2, 3};
int fourToSix[] = {4, 5, 6};
list<int> listOne;
list<int> listTwo;
#include <iostream>
#include <list>
#include <algorithm>
#include <functional>
using namespace std;
void main()
{
int oneToThree[] = {0, 1, 2};
list<int> listOne;
copy(oneToThree, oneToThree+3,
back_inserter(listOne));
copy(fourToSix, fourToSix+3,
back_inserter(listTwo));
copy(oneToThree, oneToThree+3,
back_inserter(listOne));
// 단항 연산자인 경우: transform()의 세번째 인자에
// 연산의 결과값이 들어감
transform(listOne.begin(), listOne.end(),
listOne.begin(), logical_not<bool>());
// 이항 연산자인 경우: transform()의 네번째 인자에
// 연산의 결과값이 들어감
transform(listOne.begin(), listOne.end(), listTwo.begin(),
listOne.begin(), plus<int>());
}
copy(listOne.begin(),listOne.end(),
ostream_iterator<int>(cout, " "));
수행결과: 5 7 9
}
copy(listOne.begin(),listOne.end(),
ostream_iterator<int>(cout," "));
수행결과: 1 0 0
재 사용성(3/3)

표준 라이브러리가 위 표에서 보여준 함수 객
체들을 정의할 때 사용하는 베이스 클래스들
은 새로운 함수 객체들(단항, 이항)을 만들 때
필요
template <class Arg, class Result>
struct unary_function {
typedef Arg argument_type;
typedef Result result_type;
};
template <class Arg1, class Arg2, class Result>
struct binary_function {
typedef Arg1 first_argument_type;
typedef Arg2 second_argument_type;
typedef Result result_type;
};
struct WidgetTester : binary_function<Widget, int, bool> {
public:
bool operator()(const Widget& wid, int testid) const
{ return wid.id == testid; }
};
유연성

함수를 매번 호출할 때마다 이전 호출시의 상
태를 기억해야 될 상황이 존재


일반 함수 대신, 함수 객체를 사용함
Example


generate() 알고리즘에 사용되는 발생기(generator)
발생기란 매번 호출될 때마다 다른 값을 반환하는 함수
#include <iostream>
#include <vector>
#include <algorithm>
#include <functional>
using namespace std;
class iotaGen {
public:
iotaGen(int start = 0) : current(start) { }
int operator()() { return current++; }
private:
int current;
};
void main()
{
vector<int> aVec(20);
generate(aVec.begin(), aVec.end(), iotaGen(1));
}
copy(aVec.begin(), aVec.end(), ostream_iterator<int>(cout," "));
20개의 원소를 가진 vector를 1부터 20까지
의 값으로 초기화
부정자(negator)와 바인더(binder)

부정자(negator)와 바인더(binder)는 기존에
있던 함수 객체로부터 새로운 함수 객체를 만
드는데 사용되는 함수 어댑터
부정자


not1()와 not2()는 각각 단항과 이항 조건자
(함수) 객체를 인자로 받아들여, 원래 값의 반
대 값을 내뱉는 새로운 함수 객체를 생성
부정자는 unary_function과 binary_function
클래스의 하위 클래스로 정의된 함수 객체하
고만 사용됨
#include <iostream>
#include <list>
#include <algorithm>
#include <functional>
using namespace std;
void main()
{
bool oneToThree[] = {false,true,true};
list<bool> listOne;
copy(oneToThree, oneToThree+3,
back_inserter(listOne));
// 단항 연산자인 경우: transform()의 세번째 인자에
// 연산의 결과값이 들어감
transform(listOne.begin(), listOne.end(),
listOne.begin(), not1(logical_not<bool>()));
}
copy(listOne.begin(),listOne.end(),
ostream_iterator<bool>(cout," "));
수행결과: 0 1 1
바인더

두개의 인자를 가지는 함수 객체를 받아서, 첫
번째 인자나 두번째 인자를 특정 값으로 바인
드시켜 한 개의 인자를 가지는 함수 객체를 만
들어 냄


바인더의 인자로 넘어오는 함수 객체는
binary_function 클래스의 하위 클래스에 속해야
함
bind1st() 바인더는 첫번째 인자를 바인드하고,
bind2nd()는 두번째 인자를 바인드
#include <iostream>
#include <list>
#include <algorithm>
#include <functional>
using namespace std;
void main()
{
int arr[] = {1, 2, 3, 4, 5, 6, 7, 8, 9};
list<int> listOne;
copy(arr, arr+9, back_inserter(listOne));
list<int>::iterator where = find_if(listOne.begin(), listOne.end(),
bind2nd(greater<int>(), 5));
}
copy(where,listOne.end(),
ostream_iterator<int>(cout, " "));
수행결과: 6 7 8 9
#include <iostream>
#include <list>
#include <algorithm>
#include <functional>
using namespace std;
void main()
{
int arr[] = {1, 2, 3, 4, 5, 6, 7, 8, 9};
list<int> listOne;
copy(arr, arr+9, back_inserter(listOne));
list<int>::iterator where = remove_if(listOne.begin(), listOne.end(),
not1(bind2nd(modulus<int>(), 3)));
listOne.erase(where,listOne.end());
}
copy(listOne.begin(),listOne.end(),ostream_iterator<int>(cout, " "));
수행결과: 1 2 4 5 7 8
컨테이너 클래스
Chiyoung Seo
SNU OOPSLA Lab.
목차





개요
컨테이너 선택하기
메모리 관리 이슈들
컨테이너에 포인터 저장하기
STL 없는 컨테이너 타입들
개요

표준 라이브러리는 약 10가지 종류의 컨테이
너를 제공
이름
특징
vector
임의접근이 가능하며, 뒤에서의 삽입이 빠름
list
위치에 상관없이 삽입과 삭제가 빠름
deque
임의접근이 가능하며, 앞과 뒤에서의 삽입이 빠름
set
원소들을 순서대로 관리하며, 소속검사와 삽입,삭제가 빠름
multiset
중복을 허용하는 set
map
키를 통해 값을 접근하며, 삽입과 삭제가 빠름
multimap
중복 키를 허용하는 map
stack
top에서만 삽입과 삭제가 가능
queue
삽입은 뒤쪽에서, 삭제는 앞쪽에서
priority_queue
가장 큰 값의 접근과 삭제가 빠름
컨테이너 선택하기(1/5)

콜렉션내의 값들을 어떤 방식으로 접근하는가?



임의접근이 필요하다면, vector와 deque를 사용
순차접근만으로 충분하다면, 다른 컨테이너를 써도 무방
콜렉션내의 값들에 순서를 매길 것인가?



컨테이너가 유지되는 동안 계속 순서가 유지되어야 한다면,
set을 선택
순서가 어느 한 시점에서만 중요시된다면, list나 vector에 값
들을 삽입하고, 적절한 때에 해당 컨테이너를 정렬
데이터구조 내에서 유지되는 값들의 순서가 삽입하는 순서에
관계가 있다면, stack이나 queue또는 list를 선택
컨테이너 선택하기(2/5)

실행 중에 데이터 구조의 크기가 광범위하게
변하는가?



list나 set를 선택하는 것이 가장 좋음
vector나 deque은 콜렉션으로부터 원소들을 제거
한 뒤에도 커다란 버퍼를 계속 유지
콜렉션의 크기를 추정할 수 있는가?

vector는 원하는 크기의 메모리 블럭을 미리 할당
받을 수 있음(reserve() 멤버함수를 사용)
컨테이너 선택하기(3/5)

어떤 값이 콜렉션내에 포함되어 있는지를 자
주 확인하는가?


set이나 map을 선택
콜렉션에 대해 인덱싱이 가능한가? 다시말해,
콜렉션을 키와 값의 쌍으로 생각할 수 있는가?


키들이 0과 어떤 상한선 사이의 정수값이라면,
vector와 deque를 선택
키값들이 어떤 순서가 있는 데이터형이라면(문자
나 문자열 또는 사용자 정의 타입과 같은) map을
사용
컨테이너 선택하기(4/5)

콜렉션으로부터 가장 큰 값을 찾아서 제거하
는 연산이 자주 일어나는가?


priority_queue가 최선의 컨테이너
데이터 구조의 어느 위치로 값들이 삽입되고
제거되는가?



중간쯤에서 값들이 삽입되고 제거된다면, list가 최
선의 선택
값들이 앞쪽에만 삽입된다면, deque나 list
끝에서만 삽입과 삭제가 이루어진다면, stack이나
queue
컨테이너 선택하기(5/5)

두개 이상의 수열을 하나로 합치는 일이 자주
일어나는 연산인가?


set이나 list가 유용
어느 것을 선택할 지는 순서가 유지되는가의 여부
에 따라 결정
메모리 관리 이슈들(1/3)

컨테이너(container)는 템플릿으로 설계되었
기 때문에, 다양한 타입의 원소들을 관리


정수형, 문자형, 포인터, 또는 사용자 정의(userdefined) 타입들(레퍼런스는 저장할 수 없음)
컨테이너의 메모리 관리는 프로그래머와는 무관하
게 표준 컨테이너 클래스가 알아서 자동으로 관리
메모리 관리 이슈들(2/3)




값들은 복사 생성자(copy constructor)를 사용하여
컨테이너에 배치
copy()와 같이 컨테이너로의 복사를 수행하는
generic 알고리듬들은 대입 연산자를 사용
'deep copy'가 될지, 'shallow copy'가 될지는 복사
생성자와 대입 연산자를 프로그래머가 어떻게 정의했
는지에 달려 있음
컨테이너 클래스가 데이터 구조를 위해 내부적으로
사용하는 메모리는 자동적이고 효율적으로 할당되고
반환됨
template <class InputIterator, class OutputIterator>
OutputIterator copy(InputIterator first, InputIterator last, OutputIterator result)
{
while (first != last)
*result++ = *first++;
대입 연산자를 이용하여 copy가 발생
return result;
}
Copy Generic 알고리즘
메모리 관리 이슈들(3/3)

컨테이너의 원소 타입에 대해 소멸자(destructor)가
정의되어 있다면, 컨테이너로부터 값들을 삭제할 때,
이 소멸자가 호출됨


컨테이너 전체가 소멸되는 경우에는, 남아있는 원소들에 대
해서 각각 소멸자를 호출함
포인터 값을 담고 있는 컨테이너


컨테이너는 포인터 값 자체만 관리
포인터가 참조하는 값에 대한 메모리 관리는 프로그래머의
책임


컨테이너가 해당 메모리에 대한 포인터를 간직하고 있는 동안
에는 그 메모리를 반환해서는 안됨
포인터가 컨테이너에서 삭제되면 그 포인터가 가리키고 있던
메모리를 올바르게 반환해야 함
컨테이너에 포인터 저장하기(1/2)


STL 컨테이너는 포인터 저장용으로 설계된 것이 아니
라, 객체를 담을 목적으로 설계된 자료 구조
실제로 포인터를 컨테이너에 저장해야 하는 경우가
존재




객체를 프로그램 이곳 저곳에 중복해서 만들어 놓기에는 객
체의 크기가 너무 큰 경우
이미 메모리 힙(heap)에 생성되어 있는 크키가 큰 객체를 컨
테이너에 추가하고자 하는 경우
하나의 객체를 다른 여러 컨테이너에 저장하려는 경우
base object로부터 상속된 여러 다양한 객체들을 한 컨테이
너에 저장하려는 경우
예: 도형으로부터 상속된 삼각형, 원, 직사각형, 타원등의 하
위 클래스들의 객체들을 한 컨테이너에 담고자 하는 경우
컨테이너에 포인터 저장하기(2/2)

컨테이너에 포인터를 저장하는 경우 유의사항



지역변수를 가리키는 포인터를 저장하지 말 것
포인터가 가리키는 객체들에 대한 소멸자는 프로그래머가 책
임져야 할 부분
컨테이너에 담긴 원소들에 대해 '<' 연산자를 이용하여 비교
연산을 수행하는 경우


포인터간의 대소 비교는 아무런 의미가 없으므로, 이를 반드시
고려하고 있어야 한다. 이 경우가 'smart pointer'가 아주 긴요
하게 쓰일 수 있는 경우다
set이나 map과 같이 STL 컨테이너를 생성할 때, 비교 함수
(comparator)를 템플릿 인자로 요구하는 컨테이너에 포인터
를 저장하고자 할 때는 반드시 그에 따르는 적절한 비교 함수
(또는 함수 객체)를 만들어서 넘겨주어야 함
#include
#include
#include
#include
<iostream>
<list>
<set>
<algorithm>
// list1에 있던 정수 포인터들을 set1으로 옮긴다.
// set1을 선언할 때, 포인터가 아닌, 포인터가 가리키는
// 정수간의 비교가 이루어지도록 사용자가 작성한
// 비교 함수(comparator)를 사용해야 한다.
set<int*, compare> set1;
copy(list1.begin(), list1.end(),
insert_iterator<set<int*, compare> >(set1, set1.begin()));
using namespace std;
struct compare {
bool operator()(const int* i1, const int* i2) const
{
return *i1 < *i2;
}
};
void print(int* i) { cout << " " << *i; }
// set1의 내용을 출력
cout << "Set of int* : [";
for_each(set1.begin(), set1.end(), print);
cout << "]" << endl;
}
return 0;
int main()
{
list<int*> list1;
// list1을 정수 0,1,4,9,16 각각에 대한 포인터로 초기화
for(int i = 0; i < 5; ++i)
list1.push_back(new int(i * i));
// list1의 내용을 출력
cout << "List of int*: (";
for_each(list1.begin(), list1.end(), print);
cout << ")" << endl;
실행결과:
List of int*: ( 0 1 4 9 16)
Set of int* : [ 0 1 4 9 16]
네 번째 유의 사항에 대한 예제
#include
#include
#include
#include
<iostream>
<list>
<set>
<algorithm>
int main()
{
list<int*> list1;
// list1을 정수 0,1,4,9,16 각각에 대한 포인터로 초기화
for(int i = 0; i < 5; ++i)
list1.push_back(new int(i * i));
using namespace std;
template <class ForwardIterator>
void sequence_delete(ForwardIterator first,
ForwardIterator last)
{
while (first != last)
delete *first++;
}
// list1의 내용을 출력
cout << "List of int*: (";
for_each(list1.begin(), list1.end(), print);
cout << ")" << endl;
// list1에 있던 정수 포인터들을 set1으로 옮긴다.
// set1을 선언할 때, 포인터가 아닌, 포인터가 가리키는
// 정수간의 비교가 이루어지도록 사용자가 작성한
// 비교 함수(comparator)를 사용해야 한다.
set<int*, compare> set1;
copy(list1.begin(), list1.end(),
insert_iterator<set<int*, compare> >(set1, set1.begin()));
struct compare {
bool operator()(const int* i1, const int* i2) const
{
return *i1 < *i2;
}
};
// set1의 내용을 출력
cout << "Set of int* : [";
for_each(set1.begin(), set1.end(), print);
cout << "]" << endl;
void print(int* i) { cout << " " << *i; }
}
// set 컨테이너에 담긴 int*가 가리키는 메모리 영역을 반환
sequence_delete(set1.begin(), set1.end());
//sequence_delete(list1.begin(), list1.end());
return 0;
두 번째 유의 사항에 대한 예제
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
class object {
char *str;
public:
static int count;
object() { str = 0; count++; }
object(const object& o) {
cout << "-- ctor called, ";
str = new char[10]; strcpy(str, o.str); count++;
cout << count << " object(s) remains." << endl;
}
object(const char *s) {
cout << "-- ctor called, ";
str = new char[10]; strcpy(str, s); count++;
cout << count << " object(s) remains." << endl;
}
~object() {
cout << "-- dtor called, ";
delete[] str; count--;
cout << count << " object(s) remains." << endl;
}
};
friend ostream& operator<<(ostream& os, object o)
{ return os << o.str; }
friend ostream& operator<<(ostream& os, object *o)
{ return os << o->str; }
두 번째 유의 사항에 대한 추가 예제
int object::count = 0;
template <class ForwardIterator>
void sequence_delete(ForwardIterator first, ForwardIterator last)
{
while (first != last)
delete *first++;
}
void print(object *o) { cout << o << endl; }
int main()
{
vector<object*> v;
v.push_back(new object("obj1"));
v.push_back(new object("obj2"));
cout << "Print elements..." << endl;
for_each(v.begin(), v.end(), print);
cout << "Delete elements..." << endl;
sequence_delete(v.begin(), v.end());
}
return 0;
실행결과:
-- ctor called, 1 object(s) remain(s).
-- ctor called, 2 object(s) remain(s).
Print elements...
obj1
obj2
Delete elements...
-- dtor called, 1 object(s) remain(s).
-- dtor called, 0 object(s) remain(s).
#include
#include
#include
#include
#include
<iostream>
<list>
<set>
<deque>
<algorithm>
using namespace std;
// 다음에 정의한 class X의 객체에 대한 포인터를
// 여러 종류의 STL 컨테이너에 저장하려고 함.
class X {
int i_;
public:
X(int i) : i_(i) { }
X(const X& x) : i_(x.i_) { }
~X() { }
};
X& operator=(const X& x) { i_ = x.i_; return *this;}
int operator()() const { return i_; }
bool operator==(const X& x1, const X& x2) { return x1() == x2(); }
bool operator<(const X& x1, const X& x2) { return x1() < x2(); }
// STL 컨테이너에 저장할 wrapper 클래스
class XPtrWrapper {
X* x_;
public:
XPtrWrapper(X* x = 0) : x_(x) { }
XPtrWrapper(const XPtrWrapper& xw) : x_(xw.x_) { }
~XPtrWrapper() { }
};
XPtrWrapper& operator=(const XPtrWrapper& xw) { x_ = xw.x_; return *this;}
const X* operator()() const { return x_; } // for const XPtrWrapper
X* operator()() { return x_; }
// for non-const XPtrWrapper
Smart 포인터 사용 예
bool operator==(const XPtrWrapper& xw1, const XPtrWrapper& xw2)
{
return (xw1() && xw2()) ? *xw1() == *xw2() : false;
}
bool operator<(const XPtrWrapper& xw1, const XPtrWrapper& xw2)
{
return (xw1() && xw2()) ? *xw1() < *xw2() : false;
}
void print(const XPtrWrapper& xw) { cout << " " << (*xw())(); }
int main()
{
// XPtrWrapper 5개를 배열에 저장
// (각각의 wrapper는 0,1,4,9,16를 가리킴)
XPtrWrapper bucket[5];
for(int i = 0; i < 5; i++)
bucket[i] = XPtrWrapper(new X(i * i));
random_shuffle(bucket, bucket + 5);
// list로 XPtrWrapper들을 복사
list<XPtrWrapper> list1;
copy(bucket, bucket + 5,
back_insert_iterator<list<XPtrWrapper> >(list1));
// 출력
cout << "List of XPtrWrapper: (";
for_each(list1.begin(), list1.end(), print);
cout << " )" << endl;
// set으로 XPtrWrapper들을 복사
// 앞에서도 말했지만, set을 선언할 때는 비교함수(comparator)를 반드시 지정
set<XPtrWrapper, less<XPtrWrapper> > set1;
copy(list1.begin(), list1.end(),
insert_iterator<set<XPtrWrapper, less<XPtrWrapper> > >
(set1, set1.begin())
);
// 출력
cout << "Set of XPtrWrapper : [";
for_each(set1.begin(), set1.end(), print);
cout << " ]" << endl;
// deque로 XPtrWrapper들을 복사
deque<XPtrWrapper> deque1;
copy(list1.begin(), list1.end(),
back_insert_iterator<deque<XPtrWrapper> > (deque1)
);
// 출력
cout << "Deque of XPtrWrapper : (";
for_each(deque1.begin(), deque1.end(), print);
cout << " )" << endl;
}
return 0;
실행결과:
List of XPtrWrapper:
( 16 9 0 4 1 )
Set of XPtrWrapper :
[ 0 1 4 9 16 ]
Deque of XPtrWrapper :
( 16 9 0 4 1 )
STL에 없는 컨테이너 타입들

우선 tree 콜렉션이 없다



다차원 배열도 없다


set 타입은 내부적으로 이진 검색트리의 형태를 사
용하여 구현
tree를 이용하여 해결되는 많은 문제들은 set 데이
터형으로 적절히 대체할 수 있음
vector를 원소로 하는 vector를 이용하여 대체
그래프가 없다

graph에 대한 표현은 맵을 원소로 하는 맵으로 만
들어 낼 수 있음
Vector
목차

Vector 데이터 추상

Vector 연산










Vector의 선언과 초기화
타입 정의
Vector의 첨자 연산
확장 연산과 사이즈 변환 연산
원소의 삽입과 삭제
반복자
소속검사 연산
정렬연산
유용한 Generic 알고리즘들
간단한 예제 프로그램
Vector 데이터 추상 (1/3)

시퀀스 컨테이너




vector
deque
list
정렬 연관 컨테이너


set and multiset
map and multimap
Vector 데이터 추상 (2/3)

vector 컨테이너 클래스는 기존 C 배열의 개
념을 일반화 시킴

첨자로 접근이 가능



첨자의 범위는 0부터 (원소의 갯수 - 1)
[] 연산자를 이용해서 vector에 값을 대입하거나
vector로부터 값을 추출
배열과의 차이점


vector는 일반 배열보다 자신에 관한 정보를 더 많
이 가지고 있음
vector의 크기는 동적으로 변할 수 있다
Vector 데이터 추상 (3/3)

Include 파일

vector를 사용하려면 vector 헤더 파일을 포함

#include <vector>
목차

Vector 데이터 추상

Vector 연산










Vector의 선언과 초기화
타입 정의
Vector의 첨자 연산
확장 연산과 사이즈 변환 연산
원소의 삽입과 삭제
반복자
소속검사 연산
정렬연산
유용한 Generic 알고리즘들
간단한 예제 프로그램
벡터의 선언과 초기화 (1/3)

vector가 담고 있는 원소들은 반드시 디폴트 생성자
와 복사 생성자를 정의하고 있어야 함


일부 generic 알고리즘에서 원소들간의 상등 관계나 대소 관
계를 필요로 하기 때문에, == 연산자와 < 연산자를 정의해
두는 것이 좋음
생성자

가장 일반적인 형태


원소의 개수를 정수 인자로 지정


vector<int> vec_three;
vector<int> vec_one(10);
대입에 의한 초기화

vector<int> vec_two(5, 3);
벡터의 선언과 초기화 (2/3)

생성자

복사 생성자


반복자 pair를 받는 생성자


vector<int> vec_four(vec_two);
vector<int> vec_five(aList.begin(), aList.end());
대입연산

대입 연산자의 좌변은 우변 vector의 복사본을 가짐


vec_three = vec_five;
두 가지 종류의 assign() 멤버 함수



vec_six.assign(list_ten.begin(), list_ten.end())
vec_four.assign(3, 7);
// '7'을 3개 대입
vec_five.assign(12);
// '0'을 12개 대입
벡터의 선언과 초기화 (3/3)

swap() 연산자

두 vector간에 각자 가지고 있던 원소들을 모조리
바꿔치기 할 수도 있음


vec_three.swap(vec_four);
원소의 타입이 소멸자를 정의하고 있다면, 벡
터로부터 이들 원소가 삭제될 때, 이 소멸자를
호출하게 됨
타입 정의

vector 클래스는 많은 수의 타입 정의를 포함


예: 정수 vector의 반복자 선언
 vector<int>::iterator location;
iterator 뿐만 아니라 다음과 같은 타입들도 정의되어 있음
value_type
벡터가 관리하는 원소들의 타입
const_iterator
하부 시퀀스를 변경할 수 없는 반복자
reverse_iterator
역 이동 반복자
const_reverse_iterator
위 두가지 성질을 동시에 가지는 반복자
reference
벡터 원소에 대한 참조
const_reference
원소를 변경할 수 없는 참조
size_type
컨테이너의 사이즈를 참조할 때 사용되는 비부호 정수형
difference_type
반복자간의 거리차를 나타낼 때 사용되는 부호 정수형
allocator_type
벡터의 메모리를 관리하는데 사용되는 할당기 타입
벡터의 첨자 연산

특정 인덱스 위치에서 vector가 관리하는 값을 접
근하거나 변경할 때는 첨자 연산자, “[]”를 사용



at() 멤버 함수


상수 vector를 인덱싱하면 상수 레퍼런스가 리턴
유효한 인덱스 범위를 벗어나는 vector를 인덱싱할 때
는 어떤 결과가 나올지 예측할 수 없음
vec_two.at(2) = 5;
front() and back() 멤버 함수

상수 벡터 객체일 경우는 상수 레퍼런스 값이 리턴
확장 연산과 사이즈 변환 연산 (1/3)

vector에는 세 가지의 서로 다른 size가 존재


현재 vector가 가지고 있는 원소의 개수: size()
새로운 메모리 할당 없이 vector가 확장될 수 있는 최
대 사이즈: capacity()



vector로부터 원소들을 제거할 때, vector에 대한 메모리는 반
환되지 않으며, 따라서, size는 줄어도 capacity는 그대로 임
기존에 확보된 capacity를 넘지 않으면, 삽입으로 인해 새로
메모리를 할당하지는 않음
vector가 가질 수 있는 사이즈의 상한선: max_size()

max_size()는 사용 가능한 메모리의 양 또는 size_type 자료형
이 나타낼 수 있는 최대값에 의해 제한
확장 연산과 사이즈 변환 연산 (2/3)

Vector vs. Deque(덱)



vector는 전체 원소들을 저장하기 위해 하나의 큰 메모리 블록을
사용
Deque는 많은 수의 작은 블럭들을 사용
reserve() 멤버 함수 (예: vec_five.reserve(20);)




vector의 capacity를 미리 정할 수 있는 방법을 제공
인자로 주어진 값이 현재 capacity보다 크면, 메모리 반환이 일어
나고, 인자로 주어진 값이 새로운 capacity가 됨
capacity가 이미 인자 값을 초과하고 있다면, 메모리 반환은 일어
나지 않음
메모리 반환이 일어나면, vector들의 원소를 참조하는 모든 레퍼
런스, 포인터, 반복자들은 모두 무효가 됨
확장 연산과 사이즈 변환 연산 (3/3)

empty() 멤버 함수




현재 vector의 사이즈가 0일 때 참
vector의 capacity와는 무관
size()의 리턴값과 0을 비교하는 것보다 훨씬 효과적
resize() 멤버 함수



vector의 사이즈를 인자 값으로 만들어버림
vector의 끝부분의 원소 값들이 삭제되거나 첨가
옵션인 두 번째 인자는 새로운 원소가 추가될 때의 초
기값들로 사용

vec_five.resize(12, 17);
원소의 삽입과 삭제 (1/2)

push_back() 멤버 함수



pop_back() 멤버 함수



vector의 끝에 새 원소를 첨가
현재 할당된 메모리에 공간이 남아 있다면 상수 시간 안에 수행됨
vector의 마지막 원소를 삭제
vector의 사이즈는 줄고, capacity에는 변화가 없음
insert() 멤버 함수

좀더 일반적인 삽입 연산을 수행
vector<int>::iterator where = find(vec_five.begin(), vec_five.end(), 7);
vec_five.insert(where, 12); // then insert the 12 before the 7
vec_five.insert(where, 6, 14); // insert six copies of 14
vec_five.insert(where, vec_three.begin(), vec_three.end());
원소의 삽입과 삭제 (2/2)

erase() 멤버 함수


위치를 지정하는 반복자를 이용하여 vector의 중간에
서 원소들을 삭제
두 가지 형태
(1) vec_five.erase(where);
(2) // erase from the 12 to the end
where = find(vec_five.begin(), vec_five.end(), 12);
vec_five.erase(where, vec_five.end());
반복자 (iterator)

begin()과 end() 멤버 함수



컨테이너에 대한 임의접근 반복자를 리턴
이들 연산들이 반환하는 반복자들도 원소를 삽입
하거나 삭제한 뒤에 무효가 될 수 있음
rbegin()과 rend() 멤버 함수

반대방향으로 원소들을 접근할 수 있게 해 줌
소속 검사 연산


vector는 자신이 특정 값을 포함하고 있는지
를 결정하는데 사용되는 연산을 직접 제공하
지 않음
find()나 count() generic 알고리즘 사용
int num = 0;
count (vec_five.begin(), vec_five.end(), 17, num);
if (num)
cout << "contains a 17" << endl;
else
cout << "does not contain a 17" << endl;
정렬 연산

sort() generic 알고리즘 사용
// sort ascending
sort(aVec.begin(), aVec.end());
// sort descending, specifying the ordering function explicitly
sort(aVec.begin(), aVec.end(), greater<int>() );
// alternate way to sort descending
sort(aVec.rbegin(), aVec.rend());

merge() generic 알고리즘

ordered 콜렉션을 담고 있는 vector들에 적용
// merge two vectors, printing output
merge(vecOne.begin(), vecOne.end(), vecTwo.begin(),
vecTwo.end(), ostream_iterator<int>(cout, " "));
유용한 generic 알고리즘들

대부분의 generic 알고리즘들이 벡터에 사용됨

예를 들어, vector에서의 최대값을 구하는 함수
vector<int>::iterator where =
max_element (vec_five.begin(), vec_five.end());
cout << "maximum is " << *where << endl;
용도
이름
주어진 초기값으로 벡터를 채운다.
fill
수열을 복사한다.
copy
발생기(generator)가 생성한 값을 벡터에 집어넣는다.
generate
조건을 만족하는 원소를 찾는다.
find
연속적으로 중복된 원소를 찾는다.
adjacent_find
벡터내에서 서브 시퀀스를 찾는다.
search
최대 또는 최소 원소를 찾는다.
max_element, min_element
원소의 순서를 뒤집는다.
reverse
원소들을 새로운 값들로 대치한다.
replace
가운데점을 중심으로 원소들을 순환시킨다.
rotate
원소들을 두그룹으로 쪼갠다.
partition
순열(permutation)을 생성한다.
next_permutation
벡터내에서의 ...
inplace_merge
벡터내의 원소들을 임의로 섞는다.
random_shuffle
조건을 만족하는 원소들의 갯수를 센다.
count
벡터로부터의 정보를 가지고 하나의 값을 만들어 낸다.
accumulate
두 벡터의 내적을 구한다.
inner_product
두벡터를 한쌍씩 비교하여 같은지를 검사한다.
equal
사전식 비교
lexicographical_compare
벡터에 변환을 적용한다.
transform
값들의 부분합을 구한다.
partial_sum
이웃하는 값들의 차를 구한다.
adjacent_difference
각 원소들에 대해 함수를 수행한다.
for_each
목차


Vector 데이터 추상
Vector 연산










Vector의 선언과 초기화
타입 정의
Vector의 첨자 연산
확장 연산과 사이즈 변환 연산
원소의 삽입과 삭제
반복자
소속검사 연산
정렬연산
유용한 Generic 알고리즘들
간단한 예제프로그램
에라토스테네스의 체
int main()
{
// 각 숫자들을 1로 모두 세팅한다.
const int sievesize = 100;
vector<int> sieve(sievesize, 1);
// 1로 세팅된 값들 각각에 대해 이 수의 배수들을 0으로 세팅한다.
for (int i = 2; i * i < sievesize; i++)
if (sieve[i])
for (int j = i + i; j < sievesize; j += i)
sieve[j] = 0;
}
// 1로 세팅된 숫자들만 출력한다.
for (int j = 2; j < sievesize; j++)
if (sieve[j])
cout << j << " ";
cout << endl;
실행결과: 2 3 5 7 11 13 17 19 23 29 31 37 41 43 47 53 59 61 67 71 73 79 83 89 97
List
목차

List 데이터 추상

List 연산












list의 선언과 초기화
타입 정의
list에 원소 집어넣기
원소 삭제하기
확장 및 사이즈 변환 연산
접근과 반복자
소속 검사 연산
정렬 연산
검색 연산
In Place 변환
기타 연산
예제 프로그램
List 데이터 추상 (1/2)



vector의 사이즈를 동적으로 변화시키는 것은 많은
비용을 요구하므로 자주 사용해서는 안됨
콜렉션의 사이즈는 미리 예측하기가 힘들고, 실행 중
에도 상당히 다양하게 변화함
list는 일렬로 나열된 원소들을 추상화 한 개념

삽입 연산과 삭제 연산은 항상 효율적임


콜렉션이 포함하고 있는 원소들의 갯수와는 상관없이 상수 시
간(constant time)내에 수행됨
list는 선형 구조이다

list의 원소들은 첨자에 의해서는 접근이 불가능하고, 원소들을
접근하려면 모든 값들을 선형적으로 순회해야 함
List 데이터 추상 (2/2)

list를 사용할 때는 반드시 list 헤더 화일을 포
함해야 함

#include <list>
목차

List 데이터 추상

List 연산












list의 선언과 초기화
타입 정의
List에 원소 집어넣기
원소 삭제하기
확장 및 사이즈 변환 연산
접근과 반복자
소속 검사 연산
정렬 연산
검색 연산
In Place 변환
기타 연산
예제 프로그램
list의 선언과 초기화 (1/4)


list가 담고 있는 원소들은 반드시 디폴트 생성
자와 복사 생성자를 정의하고 있어야 함
생성자

가장 간단한 형태 (디폴트 생성자)
list<int> list_one;
list<Widget *> list_two;
list<Widget> list_three;

일정수의 동일한 원소를 가지게 하는 생성자
list<int> list_four(5); // five elements, initialized to zero
list<double> list_five(4, 3.14); // 4 values, initially 3.14
list<Widget> wlist_six(4); // default constructor, 4 elements
list<Widget> list_six(3, Widget(7)); // 3 copies of Widget(7)
list의 선언과 초기화 (2/4)

생성자

다른 콜렉션에 담긴 원소들로 초기화 해주는 생성자

어떤 컴파일러들은 이를 지원하지 않음. 이러한 경우에는,
copy() generic 알고리즘을 사용한 별도의 방법을 사용
list<double> list_seven(aVector.begin(), aVector.end());
// the following is equivalent to the above
list<double> list_eight;
copy(aVector.begin(), aVector.end(), inserter(list_eight, list_eight.begin()))

복사 생성자

list<int> list_ten(list_nine);
list의 선언과 초기화 (3/4)

대입 연산

대입 연산자(=)
list<Widget> list_eleven;
list_eleven = list_six;
// values copied by assignment

assign() 멤버 함수

두 가지 형태가 존재
(1) list_six.assign(list_ten.begin(), list_ten.end());
(2) list_four.assign(3, 7);
// three copies of value seven
list_five.assign(12);
// twelve copies of value zero
list의 선언과 초기화 (4/4)

swap() 멤버 함수

두 리스트간에 서로가 가지고 있는 내용을 완전히
바꾸는 작업을 함
예: list_ten.swap(list_nine); // exchange lists nine and ten
타입 정의

list 클래스는 많은 타입 정의들을 가지고 있음
iterator
list에 대한 반복자
value_type
list에 담긴 원소의 타입
const_iterator
해당 시퀀스를 변경할 수 없는 반복자
reverse_iterator
뒷방향으로 이동하는 반복자
const_reverse_iterator
상수 반복자와 역 반복자를 합쳐 놓은 반복자
reference
원소에 대한 레퍼런스
const_reference
원소를 변경할 수 없는 레퍼런스
size_type
컨테이너의 사이즈에 사용되는 비부호형 정수 타입
difference_type
반복자간의 거리에 사용되는 부호형 정수 타입
allocator_type
list가 메모리 관리에 사용하는 할당기 타입
list에 원소 집어넣기 (1/3)

push_front()와 push_back() 멤버 함수


각각, 리스트의 앞쪽과 뒤쪽에 삽입을 해주는 함수
insert() 멤버 함수
list_eleven.insert(list_eleven.begin()); // insert default type at beginning of lis
list_eleven.insert(list_eleven.end(), Widget(8)); // insert widget 8 at end of list
// find the location of the first occurrence of the value 5 in list
list<int>::iterator location = find(list_nine.begin(), list_nine.end(), 5);
// and insert an 11 immediate before it
location = list_nine.insert(location, 11);
line_nine.insert(location, 5, 12);
// insert five twelves
// insert entire contents of list_ten into list_nine
list_nine.insert(location, list_ten.begin(), list_ten.end());
list에 원소 집어넣기 (2/3)

splice() 멤버 함수


하나의 리스트를 다른 리스트에 접목
접목은 아이템이 수신자 list에 첨가됨과 동시에 인자
로 넘어온 list로부터 삭제가 일어남
// splice the last element of list ten
list_nine.splice(location, list_ten, --list_ten.end());
// splice all of list ten
list_nine.splice(location, list_ten);
// splice list 9 back into list 10
list_ten.splice(list_ten.begin(), list_nine, list_nine.begin(), location);
list에 원소 집어넣기 (3/3)

merge() 멤버 함수



두개의 순서화(ordered) 된 리스트를 merge 해 줌
인자로 넘겨진 리스트의 값들이 순서화 된 리스트로
합쳐지고 나면, 인자로 넘겨진 리스트는 비워짐
원소들은 원래 속해 있던 리스트에서의 상대적인 순
서를 그대로 유지
// merge with explicit compare function
list_eleven.merge(list_six, widgetCompare);
//the following is similar to the above
list<Widget> list_twelve;
merge(list_eleven.begin(), list_eleven.end(), list_six.begin(), list_six.end(),
inserter(list_twelve, list_twelve.begin()), widgetCompare);
list_eleven.swap(list_twelve);
원소 삭제하기 (1/3)

pop_front()와 pop_back() 멤버 함수



각각 리스트의 시작과 끝에서 하나의 원소를 삭제
원소들에 대해 소멸자가 정의되어 있다면, 이들 원소
가 삭제될 때 소멸자가 호출
erase() 멤버 함수

두 가지 형태
list_nine.erase(location);
// erase values between the first occurrence of 5 and the following occurrence of 7
list<int>::iterator location = find(list_nine.begin(), list_nine.end(), 5);
list<int>::iterator location2 = find(location, list_nine.end(), 7);
list_nine.erase(location, location2);
원소 삭제하기 (2/3)

remove() 멤버 함수


remove_if() 멤버 함수


리스트로부터 주어진 값과 같은 모든 원소들을 삭제
주어진 조건을 만족하는 모든 값들을 삭제
remove()나 remove_if() generic 알고리즘을 사용하는
방법
list_nine.remove(4); // remove all fours
// 지원하지 않은 컴파일러도 있다
list_nine.remove_if(divisibleByThree); //remove any div by 3
// the following is equivalent to the above
list<int>::iterator location3 =
remove_if(list_nine.begin(), list_nine.end(), divisibleByThree);
list_nine.erase(location3, list_nine.end());
원소 삭제하기 (3/3)

unique() 멤버 함수


리스트에서 연속적으로 같은 값이 나오면 첫번째
원소를 제외한 나머지를 모두 삭제
인자로 이항 함수를 취할 수 있는데, 이 함수를 이
용하여 이웃 하는 원소들을 비교하여 함수가 참을
리턴 값으로 반환하면 두 번째 값을 삭제
// remove all except first from consecutive equal elements
list_nine.unique();
// 지원하지 않은 컴파일러도 있다
list_nine.unique(greater<int>()); // explicitly give comparison function
// the following is equivalent to the above
location3 = unique(list_nine.begin(), list_nine.end(), greater<int>());
list_nine.erase(location3, list_nine.end());
확장 및 사이즈 변환 연산

size() 멤버 함수


empty() 멤버 함수


컨테이너가 담고 있는 원소들의 갯수를 리턴
컨테이너가 비어있을 때 참을 리턴
resize() 멤버 함수



리스트의 사이즈를 인자로 넘겨준 값으로 바꿈
필요하다면 collection의 끝부분에서 값들을 추가
로 더하거나 삭제
생략 가능한 두 번째 인자는 새로 원소가 추가될
필요가 있는 경우에 초기값으로 사용
접근과 반복자

front()와 back() 멤버 함수



begin()과 end() 멤버 함수


각각 컨테이너의 처음 원소와 마지막 원소를 반환
삭제는 하지 않는다
순방향으로 이동할 수 있는 양방향 반복자를 리턴
rbegin()과 rend() 멤버 함수

역순으로 이동할 수 있는 양방향 반복자를 리턴
소속 검사 연산


리스트는 소속 검사를 위한 함수를 제공하지 않음
find()나 count() generic 알고리즘을 사용
int num = 0;
count(list_five.begin(), list_five.end(), 17, num);
if (num > 0)
cout << "contains a 17" << endl;
else
cout << "does not contain a 17" << endl;
if (find(list_five.begin(), list_five.end(), 17) != list_five.end())
cout << "contains a 17" << endl;
else
cout << "does not contain a 17" << endl;
정렬 연산

sort() 멤버 함수

원소들을 오름차순으로 정렬
list_ten.sort( );

// place elements into sequence
'<' 연산자 이외의 비교 연산자를 원한다면, 인자로
제공하면 됨
list_twelve.sort(widgetCompare); // sort with widget compare function
검색 연산

Generic 알고리즘 함수들


find(), find_if(), adjacent_find(), mismatch(),
max_element(), min_element(), search()
모든 경우에 이들은 반복자를 결과값으로 리턴

검색 조건을 만족하는 원소가 없으면, 위 함수들은 마지막 원
소 다음을 가리키는 반복자(past-the-end 반복자)를 리턴
In Place 변환 (1/2)

reverse() 멤버 함수


원소들의 순서를 뒤집는다
transform() generic 알고리즘 함수

입력 컨테이너와 결과 컨테이너를 같은 것으로 지정하면, 컨테이너내
의 모든 값들을 바꿀 수 있음
예; transform(list_ten.begin(), list_ten.end(), list_ten.begin(), bind1st(plus<int>(),
1));

replace()와 replace_if() generic 알고리즘 함수

리스트의 원소들을 특정 값으로 치환
In Place 변환 (2/2)

rotate() generic 알고리즘 함수
int arr[] = {1, 2, 3, 4, 5, 6, 7, 8, 9};
list<int> listOne;
copy(arr, arr+9, back_inserter(listOne));
list<int>::iterator location = find(listOne.begin(), listOne.end(), 5);
rotate(listOne.begin(), location, listOne.end());
copy(listOne.begin(),listOne.end(),ostream_iterator<int>(cout, " "));
실행결과: 5 6 7 8 9 1 2 3 4
기타 연산

for_each() generic 알고리즘 함수


콜렉션 내의 모든 원소에 대해 인자로 넘어온 함수
를 적용
accumulate() generic 알고리즘 함수

콜렉션으로부터 하나의 값을 만들어 낸다

예: 정수 리스트에 속한 모든 원소들의 합
cout << "Sum of list is: " <<
accumulate(list_ten.begin(), list_ten.end(), 0) << endl;
목차


List 데이터 추상
List 연산












list의 선언과 초기화
타입 정의
List에 원소 집어넣기
원소 삭제하기
확장 및 사이즈 변환 연산
접근과 반복자
소속 검사 연산
정렬 연산
검색 연산
In Place 변환
기타 연산
예제 프로그램
예제 프로그램(Inventory System)

Assume a business, named WorldWideWidgetWorks,
requires a software system to manage their supply of
widgets

Widgets are simple devices, distinguished by different
identification numbers:
class Widget {
public:
Widget(int a = 0) : id(a) { }
void operator = (const Widget& rhs) { id = rhs.id; }
int id;
friend ostream & operator << (ostream & out,const Widget & w)
{ return out << "Widget " << w.id; }
friend bool operator == (const Widget& lhs, const Widget& rhs)
{ return lhs.id == rhs.id; }
friend bool operator< (const Widget& lhs, const Widget& rhs)
{ return lhs.id < rhs.id; }
};
예제 프로그램(Inventory System)

The state of the inventory is represented by two lists



One list represents the stock of widgets on hand
The second is a list of widget identification types that
customers have backordered
To handle our inventory we have two commands


order() processes orders
receive() processes the shipment of a new widget
class inventory {
public:
void order (int wid);
// process order for widget type wid
void receive (int wid); // receive widget of type wid in shipment
private:
list<Widget> on_hand;
list<int> on_order;
};
예제 프로그램(Inventory System)

When a new widget arrives in shipment,


Compare the widget identification number with the list of widget
types on backorder
If found, ship the widget, otherwise add it to the stock on hand
void inventory::receive (int wid)
{
cout << "Received shipment of widget type " << wid << endl;
list<int>::iterator weneed =
find (on_order.begin(), on_order.end(), wid);
if (weneed != on_order.end())
{
cout << "Ship " << Widget(wid)
<< " to fill back order" << endl;
on_order.erase(weneed);
}
else
on_hand.push_front(Widget(wid));
}
예제 프로그램(Inventory System)

When a customer orders a new widget,

scan the list of widgets in stock to determine if the order
can be processed immediately

The general widget-testing function is written as follows:
class WidgetTester : public binary_function<Widget, int, bool> {
public:
bool operator () (const Widget & wid, int testid) const
{ return wid.id == testid; }
};

The widget order function is then written as follows:
void inventory::order (int wid)
{
cout << "Received order for widget type " << wid << endl;
list<Widget>::iterator wehave =
find_if (on_hand.begin(), on_hand.end(),
bind2nd(WidgetTester(), wid));
if (wehave != on_hand.end())
{
cout << "Ship " << *wehave << endl;
on_hand.erase(wehave);
}
else
{
cout << "Back order widget of type " << wid << endl;
on_order.push_front(wid);
}
}
Deque
목차



Deque 데이터 추상
Deque 연산
예제 프로그램
Deque 데이터 추상

Deque(double-ended queue)


전통적으로, 콜렉션의 앞과 뒤 어느 곳에서나 삽입과 삭
제가 가능한 데이터 구조들을 지칭
vector 클래스와 list 클래스가 제공하는 것들을 모두 포
함




vector와 같이 deque은 인덱싱이 가능
list와 같이 deque의 앞과 뒤에 값을 효율적으로 추가할 수 있음
list와 vector 클래스에서와 같이, deque가 관리하는 시퀀스의
중간에서 값들을 삽입할 수 있음 (비용: list < deque < vector)
Include 화일

#include <deque>
목차

Deque 데이터 추상

Deque 연산

예제 프로그램
Deque 연산 (1/2)



vector와 같은 방식으로 선언
클래스 내부에는 vector와 동일한 타입 정의를
가지고 있다
begin()과 end() 멤버 함수



양방향 반복자를 제공하는 리스트와는 달리, 임의접
근 반복자를 리턴
삽입(insert(), push_front(), push_back())
vector에 적용되는 모든 generic 알고리즘은
deque에도 사용됨 (임의접근 반복자)
Deque 연산 (2/2)

Deque는 많은 수의 작은 블럭들을 사용



메모리 블럭의 사이즈에 제한을 두고 있는 시스템에서 이는 매
우 중요한 사항
deque은 vector보다 더 많은 원소들을 포함할 수 있음
값들을 삽입할 때는, 콜렉션내의 원소와 연결되어 있던
인덱스가 바뀌게 됨

예를 들어, 3번 위치에 값을 삽입하면, 원래 3번 자리에 있던 값
은 4번 자리를 차지하게 되고, 4번 자리에 있던 값은 5번 자리로
차례로 옮겨지게 된다
목차

Deque 데이터 추상
Deque 연산

예제 프로그램

예제 프로그램 - Radix Sort (1/3)



A technique for ordering a list of positive integer
values
The values are successively ordered on digit positions,
from right to left
This is accomplished by copying the values into
"buckets," where the index for the bucket is given by
the position of the digit being sorted
예제 프로그램 - Radix Sort (2/3)

예제: 숫자 리스트

624 852 426 987 269 146 415 301 730 78 593
bucket
pass 1
pass 2
pass 3
0
730
301
78
1
301
415
146
2
852
624, 426
269
3
593
730
301
4
624
146
415, 426
5
415
852
593
6
426, 146
269
624
7
987
78
730
8
78
987
852
9
269
593
987
예제 프로그램 - Radix Sort (3/3)

전제 소스코드
void radixSort(list<unsigned int> & values)
{
bool flag = true;
int divisor = 1;
while (flag) {
vector< deque<unsigned int> > buckets(10);
flag = false;
for_each(values.begin(), values.end(),
copyIntoBuckets(divisor, buckets, flag));
accumulate(buckets.begin(), buckets.end(),
values.begin(), listCopy);
divisor *= 10;
}
}
list<unsigned int>::iterator
listCopy(list<unsigned int>::iterator c, deque<unsigned int> & lst)
{
// copy list back into original list, returning end
return copy(lst.begin(), lst.end(), c);
}
class copyIntoBuckets {
public:
copyIntoBuckets
(int d, vector< deque<unsigned int> > & b, bool & f)
: divisor(d), buckets(b), flag(f) {}
int divisor;
vector<deque<unsigned int> > & buckets;
bool & flag;
void operator () (unsigned int v)
{ int index = (v / divisor) % 10;
// flag is set to true if any bucket
// other than zeroth is used
if (index) flag = true;
buckets[index].push_back(v);
}
};
set, multiset, bitset
목차




set 데이터 추상(data abstraction)
set과 multiset 연산
예제 프로그램 - 철자 검사기
bitset 추상(abstraction)
Set 데이터 추상

Set은 트리 형태로 순서를 유지하여 값들을 관
리




원소의 삽입, 삭제, 소속검사등을 검사하는데 있어
최적화  시간 복잡도 O(logn)
삽입, 삭제와 값의 포함여부에 대한 검사가 중요한
문제에서는 set을 선택
list에서처럼, set도 사이즈에 제한을 받지 않음
두 가지 종류


set
multiset
목차

set 데이터 추상(data abstraction)

set과 multiset 연산


예제 프로그램 - 철자 검사기
bitset 추상(abstraction)
set의 선언과 초기화 (1/2)

set는 템플릿 타입으로 첫번째 인자는 원소 타입을, 두
번째 인자는 키 값을 비교하는 연산자를 명시 해야 함


두 번째 인자를 따로 명시하지 않으면, 원소 타입에 대한 lessthan 연산자(<)를 사용
원소 타입은 상등 연산자(==)와 less-than 연산자(<)를 가지고
있어야 함
set의 선언과 초기화 (2/2)

생성자, 대입 연산, swap() 멤버 함수
(1) set <int> set_one;
(2) set <int, greater<int> > set_two;
set <int> set_three(greater<int>());
(3) set <gadget, less<gadget> > gset;
set <gadget> gset(less<gadget>());
(4) set <int> set_four(aList.begin(), aList.end());
set <int> set_five(aList.begin(), aList.end(), greater<int>());
(5) set <int> set_six (set_four);
(6) set_one = set_five;
(7) set_six.swap(set_two);
// copy constructor
타입 정의

set과 multiset 클래스는 많은 타입 정의들을 포함
iterator
An general iterator of the set.
value_type
The type associated with the elements the set maintains.
const_iterator
An iterator that does not allow modification of the underlying sequence.
reverse_iterator
An iterator that moves in a backward direction.
const_reverse_iterator
A combination constant and reverse iterator.
reference
A reference to an underlying element.
const_reference
A reference to an underlying element that will not permit modification.
size_type
An unsigned integer type, used to refer to the size of containers.
value_compare
A function that can be used to compare two elements.
difference_type
A signed integer type, used to describe the distance between iterators.
allocator_type
An allocator used by the container or all storage management.
삽입 (1/2)

insert() 멤버 함수, 단 한 가지 방법이 존재


multiset의 경우에는 방금 삽입한 값을 가리키는 반복
자를 리턴
set의 경우에는 pair 타입을 리턴

첫번째 필드는 반복자이고, 두 번째 필드는 부울값으로 원소
가 삽입 되었는지의 여부를 표시
set_one.insert(18);
if (set_one.insert(18).second)
cout << "element was inserted" << endl;
else
cout << "element was not inserted " << endl
삽입 (2/2)

다른 컨테이너에 들어있는 값들을 삽입할 때
는 한 쌍의 반복자를 사용


set_one.insert(set_three.begin(), set_three.end());
Pair 데이터 구조
template <class T1, class T2>
struct pair {
T1 first;
T2 second;
pair (const T1 & x, const T2 & y) : first(x), second(y) { }
};
삭제

erase() 멤버 함수

인자는 특정 값이거나, 값을 가리키는 반복자 또는
값들의 range를 지정하는 한 쌍의 반복자일 수 있음
// erase element equal to 4
set_three.erase(4);
// erase element five
set<int>::iterator five = set_three.find(5);
set_three.erase(five);
// erase all values between seven and eleven
set<int>::iterator seven = set_three.find(7);
set<int>::iterator eleven = set_three.find(11);
set_three.erase (seven, eleven);
검색과 카운팅 (1/2)

find() 멤버 함수

특정값을 인자로 취해서 set내에 해당 값이 존재하
면, 그 값의 위치를 가리키는 반복자를 리턴


multiset의 경우에는 이들 값 중에서 적절한 값을 리턴
set내에 존재하지 않으면, end-of-set에 해당하는
반복자를 리턴
set<int>::iterator five = set_three.find(5);
if (five != set_three.end())
cout << "set contains a five" << endl;
검색과 카운팅 (2/2)

lower_bound()와 upper_bound() 멤버 함수




multiset과 같이 쓰일 때 유용
lower_bound() 는 인자로 주어진 키 값과 일치하
는 첫번째 원소를 리턴
upper_bound() 는 키 값과 일치하는 마지막 원소
를 지나서 첫번째 원소를 반환
equal_range() 멤버 함수

lower bound와 upper bound를 포함하는 반복자
의 pair를 리턴
반복자

begin()과 end() 멤버 함수

상수 반복자 형태로 리턴을 받아야 함


set의 원소에 새로운 값을 대입하게 되어 set에 대한 순서
관계가 깨지는 것을 막기 위해서임
rbegin()과 rend() 멤버 함수

역순으로 원소들을 접근하는 상수 반복자들을 생성
Set 연산 (1/3)

부분집합 검사, union, intersection, difference 연산들
은 멤버함수로 제공되지 않음


ordered 구조에 사용되는 generic 알고리즘으로 제공
부분집합 검사

includes() generic 알고리즘 함수
if (includes(set_one.begin(), set_one.end(),
set_two.begin(), set_two.end()))
cout << "set_two is a subset of set_one" << endl;
Set 연산 (2/3)

합집합과 교집합

set_union() 함수와 set_intersection() 함수
// union two sets, copying result into a vector
vector<int> v_one (set_one.size() + set_two.size());
set_union(set_one.begin(), set_one.end(),
set_two.begin(), set_two.end(), v_one.begin());
// form union in place
set<int> temp_set;
set_union(set_one.begin(), set_one.end(), set_two.begin(), set_two.end(),
inserter(temp_set, temp_set.begin()));
set_one.swap(temp_set); // temp_set will be deleted

multiset의 합집합 연산은 두 집합을 합병하는 연산, merge()와는
다르다
Set 연산 (3/3)

차집합

set_difference() 알고리즘 함수


set_symmetric_difference() 알고리즘 함수


첫번째 집합의 원소중에서 두 번째 집합에 속하지 않는 원
소들을 나타냄
두번째 집합의 원소를 제외한 첫번째 집합의 원소들과 첫번
째 집합의 원소를 제외한 두번째 집합의 원소들을 합한 것
set_union()와 사용법이 비슷함
기타 generic 알고리즘


set은 순서를 매기고, 되도록 이면 상수 반복자를 사용해
야 하기 때문에, generic 함수들의 사용에 제약이 많음
set 데이터 타입과 함께 사용될 수 있는 대표적인 유용한
generic 함수들
용도
이름
Copy one sequence into another
copy
Find an element that matches a condition
find_if
Find a sub-sequence within a set
search
Count number of elements that satisfy condition
count_if
Reduce set to a single value
accumulate
Execute function on each element
for_each
목차

set 데이터 추상(data abstraction)
set과 multiset 연산

예제 프로그램 - 철자 검사기

bitset 추상(abstraction)

예제 프로그램 – 철자 검사기

The checker takes as arguments two input streams



The first representing a stream of correctly spelled words
(that is, a dictionary)
The second a text file
소스 코드
void spellCheck (istream & dictionary, istream & text)
{
typedef set <string, less<string> > stringset;
stringset words, misspellings;
string word;
istream_iterator<string> dstream(dictionary), eof;
// first read the dictionary
copy (dstream, eof, inserter(words, words.begin()));
// next read the text
while (text >> word)
if (! words.count(word))
misspellings.insert(word);
// finally, output all misspellings
cout << "Misspelled words:" << endl;
copy (misspellings.begin(), misspellings.end(),
ostream_iterator<string>(cout, "\n"));
}
예제 프로그램 – 철자 검사기

An improvement would be to suggest alternative
words for each misspelling


There are various heuristics that can be used to discover
alternatives
The technique we will use here is to simply exchange
adjacent letters
void findMisspell(stringset & words, string & word)
{
for (int I = 1; I < word.length(); I++) {
swap(word[I-1], word[I]);
if (words.count(word))
cout << "Suggestion: " << word << endl;
// put word back as before
swap(word[I-1], word[I]);
}
}
목차

set 데이터 추상(data abstraction)
set과 multiset 연산
예제 프로그램 - 철자 검사기

bitset 추상(abstraction)


bitset 추상




bitset은 set과 vector의 cross
추상은 이진값의 집합이다.
논리적 bit별 연산을 사용하여 bitset상에 set 연
산을 수행
원소를 접근하는데 사용할 반복자를 제공하지
않음
bitset의 선언과 초기화

템플릿 인자가 타입이 아니라 정수 값임


이 값은 set이 포함하는 비트의 개수를 의미
세 가지 종류의 생성자
(1) bitset<126> bset_one;
// create a set of 126 bits
(2) bitset<126> bset_two(100); // this set has only 100 elements
(3) bitset<126> small_set("10101010"); // this set has 8 elements
접근과 검사 (1/2)


bitset의 각각의 bit는 첨자 연산을 사용하여 접근이 가능
test() 멤버 함수


any() 멤버 함수


bitset에 어느 한 bit이라도 'on' 이면 true, 아니면 false 리턴
none() 멤버 함수


특정 bit가 1(on)인지 0(off)인지 알아보기 위해 사용
bitset의 모든 bit이 ‘off’ 이면 true, 아니면 false 리턴
set() 멤버 함수


특정 bit를 on 하는데 사용
아무 인자 없이 이 함수를 호출하면 모든 비트를 'on'으로 세팅
접근과 검사 (2/2)

reset() 멤버 함수



인자로 명시된 위치의 비트를 'off'로 세팅
인자가 없으면, 모든 비트를 'off'로 세팅
flip() 멤버함수

비트를 가리키는 레퍼런스에 대한 멤버 함수로서도 제공됨
bset_one.flip(); // flip the entire set
bset_one.flip(12); // flip only bit 12
bset_one[12].flip();
// reflip bit 12

count() 멤버 함수

세팅(on)되어 있는 비트들의 갯수를 리턴
Set 연산

bit-wise 연산자를 사용하여 구현

부정 연산자(~ 연산자)


두 bitset의 교집합 연산자(& 연산자)




인자로 지정된 bitset의 원소들의 반대값을 포함하는 새로운
bitset을 리턴
bset_three = bset_two & bset_four;
bset_five &= bset_three;
두 bitset의 합집합 연산자(| 연산자)
exclusive-or 연산자(^ 연산자)
왼쪽 쉬프트 연산자(<< 연산자)과 오른쪽 쉬프트 연
산자(>> 연산자)
변환

to_ulong() 멤버 함수



bitset을 'unsigned long'으로 바꾼다
더 많은 원소를 가지는 bitset에 이 연산을 수행하
면 에러를 발생
to_string() 멤버 함수


bitset을 'string' 타입으로 바꾼다
string은 bitset의 원소갯수 만큼의 문자를 가지게
된다
map과 multimap
목차

map 데이터 추상(data abstraction)

map과 multimap 연산








map의 선언과 초기화
타입 정의
삽입과 접근
삭제
반복자
검색과 카운팅
원소 비교
예제 프로그램
Map 데이터 추상


vector나 deque와 마찬가지로 map도 인덱싱이 가능
vector나 deque에 대한 두 가지 차이점




map에서는 인덱스 값(키값)이 반드시 정수일 필요는 없고,
ordered 데이터 타입이면 가능
map은 ordered 데이터 구조 (tree-map vs. hash-map)
map은 pair를 관리하는 set으로 봐도 무방
두 가지 종류의 map


Map: 키와 그에 대응되는 값이 서로 일대일 대응
Multimap: 같은 키로 인덱싱되는 여러개의 서로 다른 원소들을
허용
목차

map 데이터 추상(data abstraction)

map과 multimap 연산








map의 선언과 초기화
타입 정의
삽입과 접근
삭제
반복자
검색과 카운팅
원소 비교
예제 프로그램
Map의 선언과 초기화 (1/2)


키(key)와 그에 대응하는 데이터 값이 컨테이너의 한 원
소를 이루는 데이터 구조
Map 템플릿의 인자



키의 타입
키에 대응되는 값의 타입
키를 비교하는데 사용될 비교 연산자를 지정

생략이 가능하고, 생략되는 경우에는 키의 타입에 정의되어 있는
less-than 연산자(<)로 지정
Map의 선언과 초기화 (2/2)

생성자


디폴트 생성자
한쌍의 반복자를 사용하여 다른 컨테이너에 담긴 값들로 초기화


반복자는 pair 타입의 값을 가리키고 있어야 함
복사 생성자
// map indexed by doubles containing strings
map<double, string, less<double> > map_one;
// map indexed by integers, containing integers
map<int, int> map_two(aContainer.begin(), aContainer.end());
// create a new map, initializing it from map two
map<int, int> map_three(map_two); // copy constructor
타입 정의

map에는 다음과 같은 타입들이 정의되어 있음
iterator
map에 대한 일반적인 반복자
key_type
map을 인덱싱할 때 사용하는 키의 타입
value_type
맵이 담고 있는 데이터 즉 key/value pair 타입
const_iterator
자신이 가리키는 원소를 변경할 수 없는 상수 반복자
reverse_iterator
역방향으로 이동하는 역반복자
const_reverse_iterator
상수 반복자와 역 반복자의 성질을 모두 가지는 반복자
reference
A reference to an underlying value.
const_reference
A reference to an underlying value that will not permit the element to be modified.
size_type
An unsigned integer type, used to refer to the size of containers.
key_compare
A function object that can be used to compare two keys.
value_compare
A function object that can be used to compare two elements.
difference_type
A signed integer type, used to describe the distances between iterators.
allocator_type
An allocator used by the container for all storage management.
삽입과 접근 (1/2)

insert() 멤버 함수

인자는 반드시 키와 값의 pair 이어야 함
이때 사용되는 pair는 map에 정의되어 있는 value_type이라
는 데이터 타입을 사용하여 만듬
예: map_three.insert(map<int>::value_type(5, 7));


한 쌍의 반복자를 사용하여 삽입을 수행
예: map_two.insert(map_three.begin(),
map_three.end());
삽입과 접근 (2/2)

첨자 연산을 통해 값을 접근 및 삽입

map에서만 가능 (multimap은 불가능)
map<int, int> map_three;
map_three[1]= 10;
map_three[2]= 20;
cout << "Index value 2 is " << map_three[2] << endl;
// now change the associated value
map_three[2] = 30;
cout << "Index value 2 is " << map_three[2] << endl;
삭제

erase() 멤버 함수


삭제될 원소를 키 값으로 지정하거나 반복자로 지정
할 수도 있음
multimap에서는 키와 연관된 모든 원소를 삭제
// erase the element(key=4)
map_three.erase(4);
// erase the element(key=5)
mtesttype::iterator five = map_three.find(5);
map_three.erase(five);
// erase all values between the key(=7) and the key(=11)
mtesttype::iterator seven = map_three.find(7);
mtesttype::iterator eleven = map_three.find(11);
map_three.erase(seven, eleven);
반복자

begin()과 end() 멤버 함수

양방향 반복자를 생성


반복자를 참조하면 키(key)와 값(value)의 pair를 얻
을 수 있음


대소 비교(<, >, <=, >=)가 허용되지 않는다는 점에 유의
필드명으로 first를 사용하면 키를, second를 사용하면 값
(value)을 얻을 수 있다
rbegin()과 rend() 멤버 함수
검색과 카운팅 (1/2)

find() 멤버함수



키를 인자로 취해서, 키/값 pair를 가리키는 반복자를
리턴
multimap의 경우에는, 가장 먼저 일치하는 키/값 pair
를 가리키는 반복자를 리턴
원하는 값을 찾지 못할 때는, past-the-end 반복자를
리턴
if (map_one.find(4) != map_one.end())
cout << "contains a 4th element" << endl;
검색과 카운팅 (2/2)

lower_bound() 멤버 함수


upper_bound() 멤버 함수


인자로 주어진 키와 일치하는 마지막 원소 바로 직후의 원소
를 가리키는 반복자를 리턴
equal_range() 멤버 함수


인자로 주어진 키와 일치하는 첫번째 원소를 가리키는 반복
자를 리턴
lower bound와 upper bound를 담고 있는 반복자의 pair를
리턴
count() 멤버 함수

인자로 주어진 키 값과 일치하는 원소의 갯수를 리턴
원소 비교

key_comp()와 value_comp() 멤버함수



각각 key 타입 또는 value 타입의 원소를 비교할 때 사
용되는 함수 객체를 반환
비교할 때 사용되는 값들이 map에 포함되어 있을 필
요는 없음
이 함수들은 컨테이너에 아무런 영향을 미치지 않는다
if (map_two.key_comp()(i, j)) // map_two.key_comp(i, j) ?
cout << "element i is less than j" << endl;
목차


map 데이터 추상(data abstraction)
map과 multimap 연산








map의 선언과 초기화
타입 정의
삽입과 접근
삭제
반복자
검색과 카운팅
원소 비교
예제 프로그램
전화 데이터베이스

telephone database is a good application for
a map



Key: the name of person or business (string)
Value: the telephone number (long)
소스 코드
typedef map<string, long, less<string> > friendMap;
typedef friendMap::value_type entry_type;
class telephoneDirectory {
public:
void addEntry (string name, long number) // add new entry to
// database
{ database[name] = number; }
void remove (string name) // remove entry from database
{ database.erase(name); }
void update (string name, long number) // update entry
{ remove(name); addEntry(name, number); }
void displayDatabase() // display entire database
{ for_each(database.begin(), database.end(), printEntry); }
void displayPrefix(int); // display entries that match prefix
void displayByPrefix(); // display database sorted by prefix
private:
friendMap database;
};
void printEntry(const entry_type & entry)
{
cout << entry.first << ":" << entry.second << endl;
}
void telephoneDirectory::displayPrefix(int prefix)
{
cout << "Listing for prefix " << prefix << endl;
friendMap::iterator where;
where =
find_if (database.begin(), database.end(),
checkPrefix(prefix));
while (where != database.end()) {
printEntry(*where);
where = find_if (++where, database.end(),
checkPrefix(prefix));
}
cout << "end of prefix listing" << endl;
}
int prefix(const entry_type & entry)
{ return entry.second / 10000; }
class checkPrefix {
public:
checkPrefix (int p) : testPrefix(p) { }
int testPrefix;
bool operator () (const entry_type & entry)
{ return prefix(entry) == testPrefix; }
};
typedef map<long, string, less<long> > sortedMap;
typedef sortedMap::value_type sorted_entry_type;
void telephoneDirectory::displayByPrefix()
{
cout << "Display by prefix" << endl;
sortedMap sortedData;
friendMap::iterator itr;
for (itr = database.begin(); itr != database.end(); itr++)
sortedData.insert(sortedMap::value_type((*itr).second,
(*itr).first));
for_each(sortedData.begin(), sortedData.end(),
printSortedEntry);
}
void printSortedEntry (const sorted_entry_type & entry)
{
cout << entry.first << ":" << entry.second << endl;
}
Stack & queue
목차

개요

stack




stack 데이터 추상(data abstraction)
stack의 선언과 초기화
예제 프로그램 - RPN 계산기
queue



queue 데이터 추상(data abstraction)
queue의 선언과 초기화
예제 프로그램 - Bank Teller 시뮬레이션
개 요 (1/2)

Stack


Queue


LIFO(Last In, First Out)
FIFO(First In, First Out)
표준 라이브러리에서는 stack과 queue가 모두 어댑터
(adaptor)이고, 이들은 실질적으로 값을 저장하는데 사용
되는 컨테이너를 바탕으로 만들어 짐


stack은 vector, list, deque로부터 만들어질 수 있음
queue는 list나 deque로부터 만들어질 수 있음
개 요 (2/2)

stack이나 queue는 반복자를 정의하지 않고
있다


하나씩 값들을 제거해보지 않고서는 콜렉션의 원소
들을 살펴볼 수 없다
stack이나 queue에 저장된 객체는 반드시 '<'
와 '==' 연산자를 모두 인식해야 한다
목차

개요

stack




stack 데이터 추상(data abstraction)
stack의 선언과 초기화
예제 프로그램 - RPN 계산기
queue



queue 데이터 추상(data abstraction)
queue의 선언과 초기화
예제 프로그램 - Bank Teller 시뮬레이션
stack 데이터 추상


stack은 전통적으로 다음 연산들을 구현한 객체로 정의
되어 있다
empty()
stack이 비었으면 참(true)을 반환
size()
stack에 담긴 원소의 갯수를 반환
top()
stack의 맨 위에 위치한 원소를 반환(지우지는 않는다)
push(newElement)
stack의 맨 위에 원소를 새로 추가
pop()
stack의 맨 위에 위치한 원소를 삭제(반환하지는 않는다)
Include 파일
예: stack을 밑받침하고 있는 컨테이너 타입이 vector 이면,
#include <stack>
#include <vector>
stack의 선언과 초기화

stack의 선언은 두개의 인자를 명시해야 함



stack이 담게 될 원소의 타입
원소들을 담는데 사용할 컨테이너 타입
컨테이너로 vector나 deque를 가장 많이 쓰고,
list도 컨테이너로 사용할 수 있다

deque를 사용하면 좀 더 빠르고, vector를 사용하면
크기가 좀 작아진다
stack<int, vector<int> > stackOne;
stack<double, deque<double> > stackTwo;
stack<Part*, list<Part* > > stackThree;
stack<Customer, list<Customer> > stackFour;
예제 프로그램 - RPN 계산기


Input to the calculator consists of a text string that
represents an expression written in RPN
RPN(Reverse Polish Notation)


A way of expressing arithmetic expressions that avoids the use
of brackets to define priorities for evaluation of operators
Ex) (3 + 5) * (7 - 2)  3 5 + 7 2 - *
Steps



Operands, that is, integer constants, are pushed on a stack of
values
As operators are encountered, the appropriate number of
operands are popped off the stack
The operation is performed, and the result is pushed back on
the stack
예제 프로그램 - RPN 계산기
class calculatorEngine {
public:
enum binaryOperator {plus, minus, times, divide};
int currentMemory ()
{ return data.top(); }
// return current top of stack
void pushOperand (int value) // push operand value on to stack
{ data.push (value); }
void doOperator (binaryOperator); // pop stack and perform
// operator
protected:
stack< int, vector<int> > data;
};
void calculatorEngine::doOperator (binaryOperator theOp)
{
int right = data.top(); // read top element
data.pop(); // pop it from stack
int left = data.top(); // read next top element
data.pop(); // pop it from stack
switch (theOp) {
case plus: data.push(left + right); break;
case minus: data.push(left - right); break;
case times: data.push(left * right); break;
case divide: data.push(left / right); break;
}
}
int main() {
int intval;
calculatorEngine calc;
char c;
while (cin >> c)
switch (c) {
case '0': case '1': case '2': case '3': case '4':
case '5': case '6': case '7': case '8': case '9':
cin.putback(c);
cin >> intval;
calc.pushOperand(intval);
break;
case '+': calc.doOperator(calculatorEngine::plus);
break;
case '-': calc.doOperator(calculatorEngine::minus);
break;
case '*': calc.doOperator(calculatorEngine::times);
break;
case '/': calc.doOperator(calculatorEngine::divide);
break;
case 'p': cout << calc.currentMemory() << endl;
break;
case 'q': return; // quit program
}
}
queue 데이터 추상


queue는 일반적으로 다음 연산들을 구현한 객체로서 정
의됨
empty()
queue가 비었으면 참(true)을 반환
size()
queue에 담긴 원소의 갯수를 반환
front()
queue의 맨 앞에 위치한 원소를 반환(삭제는 않는다)
back()
queue의 맨 뒤에 위치한 원소를 반환
push(newElement)
queue의 맨 뒤에 원소를 새로 추가
pop()
queue의 맨 앞에 위치한 원소를 삭제(반환하지는 않는다)
queue의 컨테이너 타입(예를 들어, list)에 대한 헤더파일
뿐만 아니라, queue 화일을 포함
예: #include <queue> #include <list>
queue의 선언과 초기화


queue에 대한 선언은 값을 담고 있는 컨테이너와 원소
타입을 명시
queue는 컨테이너 타입으로 list나 deque를 가장 많이
사용

list를 사용하면 코드가 작아지는 반면에, deque를 사용하면 코
드가 좀 빨라진다
queue<int, list<int> > queueOne;
queue<double, deque<double> > queueTwo;
queue<Part*, list<Part* > > queueThree;
queue<Customer, list<Customer> > queueFour;
예제 프로그램

Bank Teller 시뮬레이션


Determine how many tellers to have working during certain hours
Steps


Define objects to represent both customers and tellers
The main program is then a large loop, cycling once each
simulated minute


We assume that during peak hours there is a ninety percent chance
that a customer will arrive every minute
By executing the program several times, using various values for
the number of tellers, the manager can determine the smallest
number of tellers that can service the customers while maintaining
the average waiting time at an acceptable amount
#include
#include
#include
#include
#include
#include
<iostream>
<deque>
<vector>
<queue>
<algorithm>
<functional>
using namespace std;
unsigned int randomInteger(unsigned int n)
{
return rand() % n;
}
class Customer {
public:
Customer (int at = 0) : arrival_Time(at),
processTime(2 + randomInteger(6)) {}
int arrival_Time;
int processTime;
bool done()
// are we done with our transaction?
{ return --processTime < 0; }
operator < (const Customer & c) // order by arrival time
{ return arrival_Time < c.arrival_Time; }
operator == (const Customer & c) // no two customers are alike
{ return false; }
};
class Teller {
public:
Teller() { free = true; }
bool isFree() // are we free to service new customer?
{ if (free) return true;
if (customer.done())
free = true;
return free;
}
void addCustomer(Customer c)
{ customer = c;
free = false;
}
private:
bool free;
Customer customer;
};
// start serving new customer
void main() {
int numberOfTellers = 5;
int numberOfMinutes = 60;
double totalWait = 0;
int numberOfCustomers = 0;
vector<Teller> teller(numberOfTellers);
queue< Customer, deque<Customer> > line;
for (int time = 0; time < numberOfMinutes; time++) {
if (randomInteger(10) < 9)
line.push(Customer(time));
for (int i = 0; i < numberOfTellers; i++) {
if (teller[i].isFree() & ! line.empty()) {
Customer & frontCustomer = line.front();
numberOfCustomers++;
totalWait += (time - frontCustomer.arrival_Time);
teller[i].addCustomer(frontCustomer);
line.pop();
}
}
}
cout << "average wait:" <<
(totalWait / numberOfCustomers) << endl;
}
Priority_queue
목차




priority_queue 데이터 추상
priority_queue 연산
priority_queue의 선언과 초기화
응용 - Event-Driven 시뮬레이션

아이스크림 가게 시뮬레이션
priority_queue 데이터 추상



가장 큰 값을 신속하게 찾거나 제거할 필요가 빈번하게
발생하는 상황에서 유용하게 사용
Example
 아직 처리되지 않은 일들의 목록 관리
 운영체제에서 대기 프로세스의 리스트를 유지
Include 화일
 컨테이너 타입(예를 들어 vector)에 대한 헤더화일과
queue 헤더 화일을 포함시켜야 함
priority_queue 연산


priority queue는 T 타입의 원소를 가지고, 다음 5가지
의 연산을 구현하는 데이터 구조
push(T)
컨테이너에 새로운 값을 추가
top()
컬렉션내의 가장 큰/작은 원소의 레퍼런스를 리턴
pop()
컬렉션에서 가장 큰/작은 원소를 삭제
size()
컬렉션내에 포함된 원소들의 수를 리턴
empty()
컬렉션의 empty이면 true를 리턴
priority_queue를 구성하는데 사용되는 컨테이너는
vector와 deque가 있음
priority_queue의 선언과 초기화 (1/2)

생성자




기본 생성자
한쌍의 반복자를 취하여 다른 컨테이너의 값들로 초기화
복사 생성자
디폴트로 less-than(<) 연산자를 비교 함수로 사용

템플릿 인자나 생성자의 인자로 비교 함수를 직접 지정 가능
priority_queue< int, vector<int> > queue_one;
priority_queue< int, vector<int>, greater<int> > queue_two;
priority_queue< double, deque<double> > queue_three(aList.begin(), aList.end());
priority_queue< eventStruct, vector<eventStruct> >
queue_four(eventComparison);
priority_queue< eventStruct, deque<eventStruct> >
queue_five(aVector.begin(), aVector.end(), eventComparison);
priority_queue의 선언과 초기화 (2/2)

vector로 구축된 queue는 수행코드가 좀 더 작은 반면,


만약 수행 중에 queue내의 원소의 갯수가 큰 폭으로 변하는 경
우에는 deque로 구축된 queue가 좀 더 빨라짐
priority queue 데이터 구조는 반복자를 제공 안함

Generic 알고리즘들의 일부만이 priority queue와 같이 사용될
수 있다
응용 –
Event-Driven 시뮬레이션 (1/2)

A discrete event-driven simulation is a popular
simulation technique


Objects in the simulation model objects in the real world
A priority queue is used to store a representation of
"events" that are waiting to happen


As an event is processed, it can spawn other events


This queue is stored in order, based on the time the event
should occur, so the smallest element will always be the next
event to be processed
These subsequent events are placed into the queue as well
Execution continues until all events have been processed
응용 –
Event-Driven 시뮬레이션 (2/2)


Events can be represented as subclasses of a base
class, which we will call event
The simulation queue will need to maintain a collection
of different types of events



For this reason the collection must store pointers to events,
instead of the events themselves
We must define a new comparison function for
pointers to events
We are then ready to define the class simulation
class event {
public:
event (unsigned int t) : time(t) { }
const unsigned int time;
virtual void processEvent() = 0;
};
struct eventComparison {
bool operator () (event * left, event * right) const
{ return left->time > right->time; }
};
class simulation {
public:
simulation () : eventQueue(), time(0) { }
void scheduleEvent (event * newEvent)
{ eventQueue.push (newEvent); }
void run();
unsigned int time;
protected:
priority_queue<event *, vector<event *>, eventComparison> eventQueue;
};
void simulation::run()
{
while (! eventQueue.empty()) {
event * nextEvent = eventQueue.top();
eventQueue.pop();
time = nextEvent->time;
nextEvent->processEvent();
delete nextEvent; // free memory used by event
}
}
아이스크림 가게 시뮬레이션

Determine the optimal number of chairs that
should be provided, based on assumptions
such as the frequency that customers will
arrive, the length of time they will stay, and
so on
class storeSimulation : public simulation {
public:
storeSimulation()
: freeChairs(35), profit(0.0), simulation() { }
bool canSeat (unsigned int numberOfPeople);
void order(unsigned int numberOfScoops);
void leave(unsigned int numberOfPeople);
private:
unsigned int freeChairs;
double profit;
} theSimulation;
bool storeSimulation::canSeat
(unsigned int numberOfPeople)
// if sufficient room, then seat customers
{
cout << "Time: " << time;
cout << " group of " << numberOfPeople
<< " customers arrives";
if (numberOfPeople < freeChairs) {
cout << " is seated" << endl;
freeChairs -= numberOfPeople;
return true;
}
else {
cout << " no room, they leave" << endl;
return false;
}
}
void storeSimulation::order
(unsigned int numberOfScoops)
// serve icecream, compute profits
{
cout << "Time: " << time;
cout << " serviced order for "
<< numberOfScoops << endl;
profit += 0.35 * numberOfScoops;
}
void storeSimulation::leave
(unsigned int numberOfPeople)
// people leave, free up chairs
{
cout << "Time: " << time;
cout << " group of size " << numberOfPeople <<
" leaves" << endl;
freeChairs += numberOfPeople;
}
class arriveEvent : public event {
public:
arriveEvent (unsigned int time, unsigned int groupSize)
: event(time), size(groupSize) { }
virtual void processEvent ();
private:
unsigned int size;
};
void arriveEvent::processEvent()
{
// see if everybody can be seated
if (theSimulation.canSeat(size))
theSimulation.scheduleEvent
(new orderEvent(time + 1 + randomInteger(4), size));
}
class orderEvent : public event {
public:
orderEvent (unsigned int time, unsigned int groupSize)
: event(time), size(groupSize) { }
virtual void processEvent ();
private:
unsigned int size;
};
void orderEvent::processEvent()
{ // each person orders some number of scoops
for (int i = 0; i < size; i++)
theSimulation.order(1 + rand(3));
theSimulation.scheduleEvent
(new leaveEvent(time + 1 + randomInteger(10), size));
};
class leaveEvent : public event {
public:
leaveEvent (unsigned int time, unsigned int groupSize)
: event(time), size(groupSize) { }
virtual void processEvent ();
private:
unsigned int size;
};
void leaveEvent::processEvent ()
{
// leave and free up chairs
theSimulation.leave(size);
}
int main() {
// load queue with some number of initial events
unsigned int t = 0;
while (t < 30) {
t += rand(6);
theSimulation.scheduleEvent(
new arriveEvent(t, 1 + randomInteger(4)));
}
// then run simulation and print profits
theSimulation.run();
cout << "Total profits " << theSimulation.profit << endl;
}
string
목차

string 추상(abstraction)

string 연산










string의 선언과 초기화
사이즈(size)와 용량(capacity) 고치기
대입, 덧붙이기(append), 교환(swap)
문자 접근
반복자
삽입, 삭제, 치환(replacement)
복사와 서브string
비교
검색
예제 함수 - 텍스트 라인을 단어들로 쪼개기
string 추상(abstraction)

인덱싱이 가능한 문자들의 시퀀스


실제로는 basic_string 템플릿 클래스



typedef basic_string<char> string;
typedef basic_string<wchar_t> wstring;
string은 두 가지 종류의 사이즈가 존재



대부분의 vector 연산들을 string에 적용 가능
string이 실제로 담고 있는 문자들의 갯수
버퍼를 새로 할당하지 않고 string에 저장할 수 있는 문자의 최
대 갯수 (capacity)
Include 파일

#include <string>
목차

string 추상(abstraction)

string 연산










string의 선언과 초기화
사이즈(size)와 용량(capacity) 고치기
대입, 덧붙이기(append), 교환(swap)
문자 접근
반복자
삽입, 삭제, 치환(replacement)
복사와 서브string
비교
검색
예제 함수 - 텍스트 라인을 단어들로 쪼개기
string의 선언과 초기화 (1/2)



기본 생성자
문자열로 초기화
복사 생성자
string
string
string
string
s1;
// 변수 이름만 지정
s2("a string");
// 초기값 제공
s3 = "initial value";
// 초기값 제공
s4(s3);
// 복사 생성자 이용
string의 선언과 초기화 (2/2)



초기 capacity를 지정할 수 있는 생성자
같은 문자를 여러 개 포함하도록 string을 초기화
한 쌍의 반복자를 사용하여 초기화

반복자가 가리키는 시퀀스는 적절한 타입의 원소들을 가지고 있
어야 한다
string s6("small value", 100); // 11개의 문자를 포함, 100개까지 담을 수 있음
string s7(10, '\n');
// 10개의 개행문자로 초기화
string s8(aList.begin(), aList.end());
size와 capacity 고치기 (1/2)


size() 멤버 함수
capacity() 멤버 함수


reserve() 멤버 함수를 사용하여 변경 가능
max_size() 멤버 함수


할당할 수 있는 가장 큰 string 사이즈를 반환
사용 가능한 메모리 양에 의해 제한됨
cout << s6.size() << endl;
cout << s6.capacity() << endl;
s6.reserve(200);
// capacity를 200으로 바꾼다
cout << s6.capacity() << endl;
cout << s6.max_size() << endl;
size와 capacity 고치기 (2/2)


length() 멤버함수는 size() 멤버함수와 동일
resize() 멤버 함수


string의 사이즈를 변경할 수 있고, 이 과정에서 string의 끝
부분이 잘릴 수도 있고, 새로 문자들이 첨가될 수도 있다
empty() 멤버 함수

string에 문자가 없을 때 참
string s7("OOPSLA Lab.");
s7.resize(15);
// 맨 끝에 탭 문자들을 추가하여 사이즈를 늘린다
cout << s7.length() << endl; // 사이즈는 이제 15
if (s7.empty())
cout << "string is empty" << endl;
else
cout << s7 << endl;
assign, append, swap


다른 string을 대입할 수가 있고, C 스타일의 문자 배열
이나 개별 문자도 대입이 가능
+= 연산자도 이들 세가지 형태의 인자들과 함께 사용이
가능
s1 = s2;
s2 = "a new value";
s3 = 'x';
s3 += "yz";
// s3는 이제 "xyz"
assign, append, swap


assign()과 append() 멤버 함수
덧셈 연산자 +는 두개의 string을 연결할 때 사용


+ 연산자는 왼쪽 인자의 복사본을 만들고, 이 복사본에 오른쪽
인자를 덧붙인다
swap() 멤버 함수

두 string의 내용을 서로 바꿈
s4.assign(s2, 0, 3);
s4.append(s5, 2, 3);
// s2의 처음 3 문자를 s4에 대입한다
// 2, 3, 4번 위치에 있는 문자들을 덧붙인다
cout << (s2 + s3) << endl;
s5.swap(s4);
// s2와 s3을 덧붙인 것을 출력
// s4와 s5를 바꾼다
문자 접근


첨자 연산을 사용하여 접근하거나 대입 가능
at() 멤버함수



첨자 연산과 동일
인자로 주어진 값이 size()보다 크거나 같으면 out_of_range 예
외(exception)가 throw 됨
c_str() 멤버 함수 & data() 멤버 함수

string을 구성하는 문자 버퍼에 대한 포인터를 반환
cout << s4[2] << endl;
// s4의 2번 위치의 문자를 출력
s4[2] = 'x';
// 2번 위치의 문자를 변경
cout << s4.at(2) << endl;
// 변경된 값을 출력
char d[256];
strcpy(d, s4.c_str());
// s4를 배열 d에 복사
반복자

begin()과 end() 멤버 함수



string의 처음과 끝을 가리키는 임의접근 반복자를 리턴
rbegin()과 rend()는 역방향 반복자(?)를 리턴
반복자의 무효화

append나 insert와 같이 내부 string 버퍼의 재할당을 초래할
수 있는 연산을 수행한 뒤에는 반복자가 가리키는 값이 유효하
다고 보장할 수 없다
삽입, 삭제, 치환

insert() 멤버 함수
s2.insert(s2.begin()+2, aList.begin(), aList.end());
s3.insert(3, "abc");
// 3번 위치 뒤에 "abc" 삽입

erase() 멤버 함수
s2.erase(s2.begin()+3, s2.begin()+5);
s3.erase(4, 2);
// 4, 5번 위치 삭제

replace() 멤버 함수
s2.replace(s2.begin()+3, s2.begin()+6, s3.begin(), s3.end());
s3.replace(4, 2, "pqr"); // 4, 5번 위치를 "pqr"로 치환
복사와 서브string

copy() 멤버 함수

substring을 만들어 이를 첫번째 인자로 주어진 곳에
대입
s3.copy(s4, 2);
s5.copy(s4, 2, 3);

// s3의 2번 위치부터 끝까지를 s4에 복사
// s5의 2, 3, 4번 위치를 s4에 복사
substr() 멤버 함수

현재 string의 일부분을 나타내는 string을 리턴
cout << s4.substr(3) << endl;
cout << s4.substr(3, 2) << endl;
// 3번 위치부터 끝까지 출력
// 3, 4번 위치 출력
String 비교

compare() 멤버 함수



수신자와 인자 string간의 사전적인 비교를 수행하는
데 사용
compare() 멤버함수를 직접 호출하는 경우는 거의
없음
관계 연산자나 상등 연산들 (<, <=, >=, >,=, !=)
을 string에 대해서 사용 가능
String 검색 (1/2)

find() 멤버 함수


현 string에서 인자로 주어진 string이 처음으로 나타
나는 곳을 찾아줌
rfind() 함수가 비슷하지만, 끝에서부터 거꾸로 스캔
string s1 = "mississippi";
cout << s1.find("ss") << endl;
cout << s1.find("ss", 3) << endl;
cout << s1.rfind("ss") << endl;
cout << s1.rfind("ss", 4) << endl;
// 2를 리턴
// 5를 리턴
// 5를 리턴
// 2를 리턴
String 검색 (2/2)

find_first_of(), find_last_of(), find_first_not_of(),
find_last_not_of() 멤버함수


인자로 주어진 string을 일반적인 문자열로 보지 않고
문자의 집합으로 본다
인자로 주어진 문자집합에 속하는(또는 속하지 않는)
첫번째(또는 마지막) 문자를 찾는다
string s2 = “seoul korea”;
i = s2.find_first_of("aeiou");
j = s2.find_first_not_of("aeiou", i);
// 첫번째 모음 찾기
// 다음번 자음 찾기
목차


string 추상(abstraction)
string 연산










string의 선언과 초기화
사이즈(size)와 용량(capacity) 고치기
대입, 덧붙이기(append), 교환(swap)
문자 접근
반복자
삽입, 삭제, 치환(replacement)
복사와 서브string
비교
검색
예제 함수 - 텍스트 라인을 단어들로 쪼개기
텍스트 라인을 단어들로 쪼개기


텍스트 한 줄을 여러 개의 단어들로 쪼개는 함수
를 정의
세 개의 인자가 필요



첫번째 인자: 텍스트 라인 한 줄을 담고 있는 string
두 번째 인자: 단어들을 구분하는 문자인 구분자들을
담고 있는 string
세 번째 인자: 라인으로부터 추출된 단어들을 담게 될
string 리스트
void split(string& text, string& separators, list<string>& words)
{
int n = text.length();
int start, stop;
start = text.find_first_not_of(separators);
while ((start >= 0) && (start < n)) {
stop = text.find_first_of(separators, start);
if ((stop < 0) || (stop > n)) stop = n;
words.push_back(text.substr(start, stop - start));
start = text.find_first_not_of(separators, stop+1);
}
}
split() 함수 소스
Generic 알고리즘
목차








개요
초기화 알고리즘
검색 연산
In-Place 변환
삭제 알고리즘
스칼라 생성 알고리즘
시퀀스 생성 알고리즘
기타 알고리즘
개요 (1/4)
이름
용도
초기화 알고리즘
fill
시퀀스를 초기값으로 채우기
fill_n
n개의 자리를 초기값으로 채우기
copy
시퀀스를 다른 시퀀스에 복사하기
copy_backward
시퀀스를 다른 시퀀스에 복사하기
generate
생성기(generator)를 사용하여 시퀀스를 초기화하기
generate_n
생성기(generator)를 사용하여 n개의 자리를 초기화하기
swap_ranges
두 병렬 시퀀스의 내용 뒤바꾸기
검색 알고리즘
find
인자값과 일치하는 원소 찾기
find_if
조건을 만족하는 원소 찾기
adjacent_find
연달아 중복된 원소 찾기
find_first_of
시퀀스내에서 다른 시퀀스에 속하는 멤버중 가장 먼저 발견되는 것 착지
find_end
시퀀스내에서 서브시퀀스의 마지막 발생 찾기
search
시퀀스내에서 서브 시퀀스 찾기
max_element
시퀀스에서 최대값 찾기
min_element
시퀀스에서 최소값 찾기
mismatch
두 시퀀스를 비교하여 불일치되는 곳 찾기
개요 (2/4)
in-place 변환
reverse
시퀀스의 원소 뒤집기
replace
특정값들을 다른 값으로 치환
replace_if
조건을 만족하는 원소들을 치환
rotate
한 점을 중심으로 원소들을 순환
partition
원소들을 두그룹으로 쪼개기
stable_partition
순서를 그대로 유지하며 쪼개기
next_permutation
다음 순열 생성하기
prev_permutation
이전 순열 생성하기
inplace_merge
두개의 이웃한 시퀀스를 하나로 합치기
random_shuffle
시퀀스 내의 원소들을 임의로 재배치하기
삭제 알고리즘
remove
조건을 만족하는 원소 삭제
unique
중복되는 원소들 중 첫번째 것만 남기고 모두 삭제
개요 (3/4)
스칼라 생성 알고리듬 - 13.6절
count
값과 일치하는 원소들을 카운트
count_if
조건을 만족하는 원소들을 카운트
accumulate
시퀀스로부터 스칼라값 얻기
inner_product
두 시퀀스의 내적
equal
두 시퀀스의 상등 검사
lexicographical_compare
두 시퀀스를 비교
시퀀스 생성 알고리듬 - 13.7절
transform
각 원소들을 변환
partial_sum
부분합들의 시퀀스를 생성
adjacent_difference
인접차들의 시퀀스를 생성
기타 연산 - 13.8절
for_each
콜렉션내의 원소 각각에 대해 함수를 적용
개요 (4/4)

Include 화일


대다수의 함수가 algorithm 헤더 화일에 정의
accumulate(), inner_product(), partial_sum(),
adjacent_difference()는 numeric 헤더 화일에 정
의
목차

개요

초기화 알고리즘






검색 연산
In-Place 변환
삭제 알고리즘
스칼라 생성 알고리즘
시퀀스 생성 알고리즘
기타 알고리즘
초기화 알고리즘

새로 생성한 시퀀스를 특정 값으로 초기화할
때 사용됨

fill()


copy()


한개의 값으로 여러 원소들을 초기화
다른 컨테이너에 담긴 값들로 초기화
generate()

함수를 호출하여 얻어낸 값으로 초기화
시퀀스를 초기값으로 채우기


fill()과 fill_n() 알고리즘은 시퀀스를 정해진 값으로 초기
화하거나 재 초기화하는데 사용

void fill(ForwardIterator first, ForwardIterator last, const T&);

void fill_n(OutputIterator, Size, const T&);
fill()과 fill_n() 알고리즘은 표준 라이브러리가 제공하는
모든 컨테이너 클래스에 사용 가능

set과 같은 정렬 컨테이너는 삽입 반복자와 함께 사용해야 함
void main()
{
// 예1, 배열을 초기화한다.
char buffer[100], * bufferp = buffer;
fill(bufferp, bufferp + 100, '\0');
fill_n(bufferp, 10, 'x');
// 예2, list를 초기화한 뒤, 추가로 덧붙인다.
list<string> aList(5, "nothing");
fill_n(inserter(aList, aList.begin()), 10, "empty");
// 예3, list내의 값들을 덮어쓴다.
fill(aList.begin(), aList.end(), "full");
// 예4, 콜렉션의 일부를 채운다.
vector<int> iVec(10);
generate(iVec.begin(), iVec.end(), iotaGen(1));
vector<int>::iterator & seven =
find(iVec.begin(), iVec.end(), 7);
fill(iVec.begin(), seven, 0);
}
fill() & fill_n() 함수 사용 예제
시퀀스를 다른 시퀀스에 복사하기
(1/3)

copy()와 copy_backward() 알고리즘




OutputIterator copy(InputIterator first, InputIterator last,
OutputIterator result);
BidirectionalIterator copy_backward (BidirectionalIterator first,
BidirectionalIterator last, BidirectionalIterator
result);
copy() 알고리즘의 리턴 값은 복사된 시퀀스의 끝을
가리키는 포인터
여러 개의 복사본을 덧붙이는 방법

copy() 연산의 리턴 값을 다음 copy()의 시작 반복자로 사
용하면 된다
시퀀스를 다른 시퀀스에 복사하기
(2/3)

복사 알고리즘은 다음과 같은 용도에 주로 사용





전체 시퀀스를 새로운 시퀀스에 복사한다.
현 시퀀스의 부 시퀀스를 생성한다.
시퀀스에 원소를 추가한다.
입력에서 출력으로 시퀀스를 복사한다.
시퀀스를 다른 형태로 바꾼다
#include <iostream>
#include <list>
#include <algorithm>
// 예4, copy()를 사용하여 배열을 list로 바꾼다.
list<char> char_list;
copy(bufferp, bufferp + strlen(buffer),
inserter(char_list, char_list.begin()));
char *big = "big ";
copy(big, big + 4, inserter(char_list, char_list.begin()));
using namespace std;
void main() {
char *source = "reprise";
char *surpass = "surpass";
char buffer[120], *bufferp = buffer;
// 예1, 그냥 단순한 복사
copy(source, source + strlen(source) + 1, bufferp);
char buffer2[120], *buffer2p = buffer2;
*copy(char_list.begin(), char_list.end(), buffer2p) = '\0';
cout << buffer2 << endl;
}
// 예2, 자기 자신으로의 복사
copy(bufferp + 2, bufferp + strlen(buffer) + 1, bufferp);
int buflen = strlen(buffer) + 1;
copy_backward(bufferp, bufferp + buflen,
bufferp + buflen + 3);
copy(surpass, surpass + 3, bufferp);
// 예3, 출력으로 복사
copy(bufferp, bufferp + strlen(buffer),
ostream_iterator<char>(cout));
cout << endl;
실행결과
surprise
big surprise
copy() & copy_backward() 함수 사용 예제
시퀀스를 다른 시퀀스에 복사하기
(3/3)

copy_backward() 함수


시퀀스의 끝에서부터 앞쪽으로 복사를 거꾸로 해 나간다
복사된 뒤에 원소들의 상대적인 위치는 바뀌지 않는다
발생기가 생성한 값으로 시퀀스 초기화


generate()과 generate_n()은 시퀀스를 초기화하거나
재 초기화하는데 사용
 void generate(ForwardIterator, ForwardIterator,
Generator);
 void generate_n(OutputIterator, Size, Generator);
고정된 인자 값 대신에 발생기가 생성한 값들을 사용
// 'L_ddd' 형태의 유일한 라벨을 생성하는 발생기
string generateLabel()
{
static int lastLabel = 0;
char labelBuffer[80];
ostrstream ost(labelBuffer, 80); // #include <strstrea.h>를 포함해야함
ost << "L_" << lastLabel++ << '\0';
return string(labelBuffer);
}
void generate_example()
{
// 예1, 라벨의 list를 생성
list<string> labelList;
// list 선언시 사이즈를 명시하지 않았으므로 삽입반복자를 사용
generate_n(inserter(labelList, labelList.begin()),
4, generateLabel);
// 예2, 정수들의 시퀀스를 생성
vector<int> iVec(10);
// vector 선언시 사이즈를 명시했으므로 일반 반복자를 사용
generate(iVec.begin(), iVec.end(), iotaGen(2));
generate_n(iVec.begin(), 5, iotaGen(7));
}
generate() & generate_n() 함수 사용 예제
두개의 구간에 속한 원소들 서로
뒤바꾸기 (1/2)

템플릿 함수 swap()은 타입이 동일한 두 객체의 값을 교
환하는데 사용
template <class T> void swap(T& a, T& b)
{
T temp(a);
a = b;
b = temp;
}

iter_swap() 함수는 swap()을 보다 일반화한 것으로 교
환할 값을 반복자로 가리킨다
두개의 구간에 속한 원소들 서로
뒤바꾸기 (2/2)

swap_ranges() 알고리즘은 교환의 적용범위를 전체 시
퀀스로 확장


ForwardIterator swap_ranges (ForwardIterator first,
ForwardIterator last, ForwardIterator
first2);
두 번째 구간은 적어도 첫번째 구간만큼의 원소를 가져
야 한다
void swap_example()
{
// 먼저 두개의 시퀀스를 생성
int data[] = {12, 27, 14, 64}, *datap = data;
vector<int> aVec(4);
generate(aVec.begin(), aVec.end(), iotaGen(1));
// swap()과 iter_swap()
swap(data[0], data[2]);
vector<int>::iterator last = aVec.end(); last--;
iter_swap(aVec.begin(), last);
// 시퀀스 전체를 교환
swap_ranges(aVec.begin(), aVec.end(), datap);
}
swap() 함수 사용 예제
What should you study next?

More generic 알고리즘




검색 연산, In-Place 변환, 삭제 알고리즘, 스칼라 생성 알고리즘,
시퀀스 생성 알고리즘, 정렬 콜렉션 알고리듬
예외 처리(exception handling)
complex
…