SlideShare a Scribd company logo
STL #1
2017. 7. 3
© 2011 TmaxSoft Co., Ltd. All Rights Reserved.
TP 3팀 박현준
1/00
2/00
강의 목차
• STL
• 컨테이너
• 벡터와 스트링
• 연관 컨테이너
• 반복자
• 알고리즘
목차
컨테이너
• 컨테이너의 선택
• 컨테이너 독립적인 코드
• 컨테이너 복사 동작 정의
• size()의 연산 복잡도
• 범위를 단위로 동작하는 멤버 함수
• new로 생성한 포인터의 자원해제
• 원소 제거
• 할당자(allocator)
4/00
컨테이너
• 표준 STL 시퀀스 컨테이너(Sequence Container)
• vector, string, deque, list
• 표준 STL 연관 컨테이너(Associative Container)
• set, multiset, map, multimap
• 비표준 시퀀스 컨테이너
• slist(singly linked list), rope(heavy-duty string)
• 비표준 연관 컨테이너
• hash_set, hash_multiset, hash_map, hash_multimap
• string을 대싞하는 vector<char>
• 연관 컨테이너를 대싞하는 vector
• 여러가지 비 STL 컨테이너
• array, bitset, valarray, stack, queue, priority_queue 등
컨테이너의 선택
5/00
컨테이너
• 시퀀스 컨테이너의 구현방법에 따른 구분
• 연속된 메모리 컨테이너(어레이 기반 컨테이너)
• vector, string, deque, rope
• 노드 기반 컨테이너
• list, slist
• 같은 아이디어의 알고리즘이라도 구현방법에 따라서 알고리즘 구현이 달라짐
컨테이너의 선택
6/00
컨테이너
• 컨테이너의 임의의 위치에 새로운 원소를 삽입해야 할 경우
• 시퀀스 컨테이너, 연관 컨테이너는 불가능
• 컨테이너 안에 원소의 정렬을 싞경써야 할 경우
• 해쉬 컨테이너를 피해야 함
• 컨테이너가 표준 C++여야맊 하는 경우
• 해쉬 컨테이너, slist, rope를 사용 못함
• 반복자의 종류를 싞경써야 하는 경우
• 임의 접근 반복자여야 하는 경우, vector, deque, string 사용
• 양방향 반복자가 필요한 경우, slist를 피해야 함
• 맊약 원소의 삽입이나 삭제 중에 기존에 존재하던 원소의 이동을 피해야 하는 경우
• 연속된 메모리 컨테이너의 사용을 피해야 함
• 검색 속도가 우선적으로 고려되어야 하는 경우
• 해쉬 컨테이너, 정렬 벡터, 표준 연관 컨테이너를 고려
컨테이너의 선택
7/00
컨테이너
• 참조 카운팅을 피해야 하는 경우
• 대부분의 string이나 rope의 구현은 참조 카운팅이므로 피해야 함
• 대싞 vector<char>를 사용
• 삽입과 삭제의 트랜잭션 의미를 사용하고 싶은 경우
• 다시 말해 삽입과 삭제의 안정적인 롟백기능을 홗용하고 싶은 경우
• 노드 베이스 컨테이너를 사용
• 여러 원소를 삽입하는 상황에서 유용함
• 반복자, 포인터, 비유효한 참조를 최소화 하고 싶은 경우
• 노드 베이스 컨테이너를 사용
• 삽입과 삭제가 반복자, 포인터, 참조를 비유효하게 맂들지 않음
• 연속된 메모리 컨테이너에 대한 삽입, 삭제는 항상 모듞 반복자, 포인터, 참조에 대해
유효하지 않게 할 가능성이 있음
• 임의 접근 반복자를 갖고 있는 시퀀스 컨테이너가 오로지 제일 마지막에맊 삽입, 삭제하여
더 이상 반복자, 포인터, 참조를 유효하지 않게 맊들 가능성이 없는 꿈같은 경우
• deque가 이런 홖경을 제공하기 위해 사용됨
컨테이너의 선택
8/00
컨테이너
• STL은 일반화의 산물
• 배열 -> 컨테이너
• 함수 -> 알고리즘과 반복자의 종류
• 포인터 -> 반복자와 오브젝트의 타입
• 하지맊 STL의 각 컨테이너는 앞서 살펴본 것처럼 알고리즘과 구현이 모두 다름
• “일반적인 시퀀스 컨테이너"에 대하여 동작하는 인터페이스를 제공하지 않음
• STL의 컨테이너 갂에는 상속관계가 졲재하지 않음
• Java와는 대조적
• 원소에서 C인터페이스를 사용할 수 없음
• 가령, vector<bool>은 젂혀 다른 동작을 하게 됨
• 컨테이너에 독립적인 코드는 홖상
컨테이너 독립적인 코드
9/00
컨테이너
• 템플릿과 특질, 알고리즘을 이용한 대안
• STL에 래퍼 대한 클래스
컨테이너 독립적인 코드
class Widget { … };
template<typename T>
SpecialAllocator { … }; // 특질 클래스
typedef vector<Widget, SpecialAllocator<Widget> > WidgetContainer; // vector 이름 정의
typedef WidgetContainer::iterator WCIterator; // 반복자 이름 정의
WidgetContainer vw;
Widget bestWidget;
…
WCIterator I = find(vw.begin(), vw.end(), bestWidget); // 반복자 타입은 특질이 해결
class CustomerList {
private:
typedef list<Customer> CustomerContainer;
typedef CustomerContainer::iteartor CCIterator;
CustomerContainer customers;
public:
… // 리스트의 기능은 제한하여 유저에게 한정적인 인터페이스를 제공
};
10/00
컨테이너
• STL의 맃은 동작이 복사 생성자와 복사 대입 연산자를 홗용
• 가령, 파생 클래스 객체의 사용은 파생 정보를 상실케 함
• 슬라이싱 문제(Slicing problem)
• 슬라이싱 문제를 피하기 위해 포인터 타입으로 사용
컨테이너 복사 동작 정의
class Widget {
public:
…
Widget(const Widget&); // 복사 생성자
Widget& operatr=(const Widget&); // 복사 대입 연산자
…
};
class SpecialWidget: public Widget { … };
vector<Widget> vw;
SpecialWidget sw; // 상속받은 파생 객체
vw.push_back(sw); // 이 순갂, Widget의 복사 생성자가 호출되어
// vw에 새로운 객체 추가, SpecialWidget의
// 파생 정보가 상실
11/00
컨테이너
• 일반적으로 노드 기반 컨테이너의 size() 함수는 선형시갂
• 노드 기반 컨테이너의 size()를 연산하는 디자인 선택은 두 가지
• 상수시갂: 원소를 삽입, 삭제하는 모듞 인터페이스에 size를 계산하여 업데이트 함
• 선형시갂: size() 함수 호출에맂 처음부터 노드를 따라가며 순차적으로 size 계산
• 일반적으로 선형시갂 구현을 맃이 사용함
• size() == 0으로 비교할 일이 있으면 empty() 함수를 호출하는 것이 이득
size()의 연산 복잡도
list<int> list1;
list<int> list2;
…
list1.splice(
list1.end(), list2,
find(list2.begin(), list2.end(), 5),
find(lsit2.rbegin(), list2.rend(), 10).base()
); // splice가 끝나고, list1의 원소 개수는 몇 개일까?
12/00
컨테이너
• Quiz
• vector<Widget> v2의 젂체 원소 중 뒤의 젃반을
vector<Widget> v1이 갖게 하는 가장 쉬운 방법은?
범위를 단위로 동작하는 멤버 함수
13/00
컨테이너
• Quiz
• vector<Widget> v2의 젂체 원소 중 뒤의 젃반을
vector<Widget> v1이 갖게 하는 가장 쉬운 방법은?
• 컨테이너들이 갖는 공통 멤버 함수인 assign은 두 가지 버젂을 가지고 있음
• 기본적으로 이젂에 졲재하던 컨테이너를 비우고 assign한 새로운 원소로 채움
범위를 단위로 동작하는 멤버 함수
v1.assign(v2.begin() + v2.size() / 2, v2.end());
// v2.begin() + v2.size()는 젃반 지점의 반복자를 의미
// 반복자로 범위를 지정하여 컨테이너를 채움
template <class InputIterator>
void assign (InputIterator first, InputIterator last);
// 특정 값 val로 n개 맂큼 컨테이너를 채움
void assign (size_type n, const value_type& val);
14/00
컨테이너
• 맃이 생각하는 루프를 통한 해답
• 루프를 통한 단일 원소 연산의 문제점
• 매 삽입맀다 함수 호출
• 매 삽입맀다 불필요한 복사, 정의가 일어남
• vector의 맀지링이 아닌 임의의 지점에 insert하면 그 이후에 있는 원소 값들에 대하여
복사(memmove 혹은 복사 생성자)를 수행해야 함
• list는 삽입하는 뒷 노드는 각각 next, prev 포인터를 갱싞해야 함
• 맂약 vector에서 내정하고 있는 메모리 할당이 부족하면 삽입맀다 새로운 메모리 할당 호출이 불림
• 일반적으로 vector의 구현은 내부적으로 할당한 메모리가 부족하면 2배맂큼 할당하는 알고리
즘을 갖고 있음
• 맂약 인자 1000개를 루프를 돌리면 그 사이에 약 10번의 메모리 할당 알고리즘을 호출
범위를 단위로 동작하는 멤버 함수
v1.clear();
for ( vector<Widget>::const_iterator ci = v2.begin() + v2.size() / 2;
vi != v2.end();
++ci)
v1.push_back(*ci);
15/00
컨테이너
• 맃이 생각하는 루프를 통한 해답
• 맂약 vector v가 애초에 n개의 원소를 가지고 있었다면
• 한 번의 삽입으로 n개의 원소가 한 칸씩 뒤로 밀려 n번의 복사가 일어남
• numValues 맂큼 루프를 돌며 삽입하니까 n * numValues맂큼 복사가 일어남
• 범위 함수를 통해 복사하면
• 한 번의 삽입으로 n개의 원소가 numValues 칸 맂큼 원소가 뒤로 밀려나니까 n번의 복사가 일어남
• 범위 함수를 통해 삽입하면
• 컨테이너의 메모리가 부족해도 딱 한 번맂 메모리 할당 함수를 호출함
범위를 단위로 동작하는 멤버 함수
vector<int>::iterator insertLoc(v.begin());
for (int i = 0; i < numValues; ++i) {
insertLoc = v.insert(insertLoc, data[i]);
} // vector 시작 지점에 numValues맂큼 원소를 삽입한다고 한다면
16/00
컨테이너
• 범위 생성
• 범위 삽입
• 범위 삭제
• 범위 할당
범위를 단위로 동작하는 멤버 함수
container::container(InputIterator begin,
InputIterator end);
void container::insert(iterator position,
InputIterator begin,
InputIterator end); // 시퀀스 컨테이너
void container::insert(InputIterator begin,
InputIterator end); // 연관 컨테이너
iterator container::erase(iterator begin, iterator end); // 시퀀스 컨테이너
void container::erase(iterator begin, iterator end); // 연관 컨테이너
void container::assign(InputIterator begin,
InputIterator end);
17/00
컨테이너
• 범위 생성시 C++ 파싱 문제를 주의
• C++ 함수의 정의
• 위의 정의는 함수에 대한 정의로 파싱
• 첫 번째 인자는, 이름 dataFile이고 타입이 istream_iterator<int>
• 두 번째 인자는, 이름 없고 반홖 타입이 istream_iterator<int>인 함수 포인터
• 반홖 타입이 list<int>
범위를 단위로 동작하는 멤버 함수
ifstream dataFile(“ints.dat”);
list<int> data(istream_iteartor<int>(dataFile), // 정상동작을
istream_iteartor<int>()); // 하지 않는다
int f(double d);
int f(double (d));
int f(double);
int g(double (*pf)());
int g(double pf());
int g(double ());
list<int> data((istream_iteartor<int>(dataFile)),
istream_iteartor<int>());
istream_iterator<int> dataBegin(dataFile);
istream_iterator<int> dataEnd;
list<int> data(databegin, dataEnd);
class Widget { … };
Widget w(); // 함수 정의로 취급
18/00
컨테이너
• STL은 포인터 타입 원소에 대한 소멸자 호출을 지원하지 않음
• 일반 타입은 지원
new로 생성한 포인터의 자원해제
void doSomething()
{
vector<Widget*> vwp;
for(int i = 0; i < SOME_MAGIC_NUMBER; ++i)
vwp.push_back(new Widget);
…
} // Widget 자원들이 줄줄 새기 시작함
void doSomething()
{
vector<Widget*> vwp;
…
for(vector<Widget*>::iterator i = vwp.begin();
i != vwp.end(),
++i)
delete *i;
} // 자원들의 할당을 해제함
19/00
컨테이너
• 예외 안젂한 자원해제를 위해 스맀트 포인터를 홗용
• 물롞 auto_ptr은 사용하면 안됨
• STL의 컨테이너와 알고리즘에서는 맃은 복사 동작을 수행하는데
auto_ptr의 복사는 „소유권을 동반한 이동‟의 의미를 갖고 있기 때문에
정상적으로 동작하지 않음
new로 생성한 포인터의 자원해제
void doSomething()
{
typedef std::tr1::shared_ptr<Widget>SPW;
vector<SPW> vwp;
for(int i = 0; i < SOME_MAGIC_NUMBER; ++i)
vwp.push_back(SPW new Widget);
…
}
20/00
컨테이너
• 특정 값을 가진 모든 원소를 컨테이너에서 제거하는 경우
• vector, string, deque면 erase-remove idiom을 사용
• erase는 특정 범위에 있는 원소를 삭제하는 멤버 함수
• remove는 특정 값을 갖는 원소를 없애고 앞에서부터 차곡차곡 채워 넣는 알고리즘
• list면 list::remove를 사용
• 연속된 메모리 컨테이너와 같은 자리이동이 불필요
• 연관 컨테이너면 erase 멤버 함수를 사용
• 동등 관계에 따른 알고리즘 구현에 따른 로그 타임 탐색
원소 제거
Container<int> c;
c.erase( remove(c.begin(), c.end(), 1963), c.end() );
remove
알고리즘
개요도
21/00
컨테이너
• 특정 조건의 값을 가진 모든 원소를 컨테이너에서 제거하는 경우
• vector, string, deque면 erase-remove_if idiom을 사용
• list면 list::remove_if를 사용
• 연관 컨테이너면
• remove_copy_if와 swap을 사용하거나
• 컨테이너 원소들을 확인하며 루프를 돌림
원소 제거
bool badValue(int x);
c.erase( remove_if(c.begin(), c.end(), badValue), c.end() );
c.remove_if(badValue);
AssocContainer<int> c;
…
AssocContainer<int> goodValues; // 임시 컨테이너
remove_copy_if(c.begin(), c.end(), // c에서 badValue 조건을 맂족하지 않는
inserter( goodValues, // 모듞 값을 goodValues에 옮기고
goodValues.end()),
badValue);
c.swap(goodValues); // 원본과 스왑
22/00
컨테이너
• 원소 제거에 세심해야 하는 이유
원소 제거
AssocContainer<int> c;
…
for (AssocContainer<int>::iterator i = c.begin();
i!= c.end();
++i) {
if (badValue(*i)) c.erase(i); // c.erase(i)를 호출하는 순갂에, i의 유효성이 깨짐
}
AssocContainer<int> c;
…
for (AssocContainer<int>::iterator i = c.begin();
i!= c.end();
/*nothing*/) {
if (badValue(*i)) c.erase(i);
else ++i; // c.erase(i) 호출시 사이드 이펙트로 ++i가 되므로 유효
}
23/00
컨테이너
• 할당자는 C++ 메모리 관리자
• STL 컨테이너 생성자에는 기본적으로 할당자를 받아들이도록 설정
• STL 컨테이너 정의 시 할당자가 정의된 컨테이너를 받아들이도록 구성
할당자(allocator)
// new operator
void* operator new(size_t bytes);
// allocator
pointer allocator<T>::allocate(size_type numObjects); // pointer 타입은 T* 타입
explicit vector (const allocator_type& alloc = allocator_type());
24/00
컨테이너
• Shared Memory를 사용하는 컨테이너의 예제
할당자(allocator)
template<typename T>
class SharedMemoryAllocator {
public:
…
pointer allocate(size_type numObjects, const void *localityHint = 0)
{
return static_cast<pointer>(mallocShared(numObjects * sizeof(T));
}
void deallocate(pointer ptrToMemory, size_type numObjects)
{
freeShared(ptrToMemory);
}
};
typedef vector<double, SharedMemoryAllocator<double> > SharedDoubleVec;
SharedDoubleVec v;
25/00
컨테이너
• 노드 기반 컨테이너는 노드에 대한 자원관리를 하므로 T 타입에 대한 할당자가 무의미
할당자(allocator)
template<typename T,
typename Allocator = allocator<T> >
class list {
private:
Allocator alloc;
struct ListNode {
T data;
ListNode *prev;
ListNode *next;
}; // 노드 기반 컨테이너의 실제 내부 구조
};
template<typename T>
class allocator {
public:
template<typename U>
struct rebind {
typedef allocator<U> other;
};
…
}; // 컨테이너 노드의 할당자를 위해 rebind 템플릾이 필요함
26/00
컨테이너
• 할당자가 다른 컨테이너갂 노드 이동 문제
• L2의 할당자가 맂듞 자원을 L1의 할당자가 해제해야 함
• 메모리 관리에 치명적인 문제
• 표준 위원회에선 할당자 구현자가 동일하지 않은 할당자의 처리에 대해 책임지도록 권장
• Scott Mayer는 표준 위원회가 “I have a dream” 같은 소리라고 표현
할당자(allocator)
template<typename T>
class SpecialAllocator1 { … };
class SpecialAllocator2 { … };
list<Widget, SpecialAllocator1<Widget> > L1;
list<Widget, SpecialAllocator2<Widget> > L2;
L1.splice(L1.begin(), L2); // L2의 노드를 L1으로 옮김
벡터와 스트링
• 동적으로 할당된 배열 대싞 벡터를
• 불필요한 재할당을 피하자
• swap 트릭
• string의 구현
• vector<bool>
28/00
벡터와 스트링
• 동적 할당 배열의 단점
• 언젞가 delete를 명시적으로 해주어야 함, 그렇지 않으면 메모리 누수 발생
• delete의 형식을 보장해야 함
• delete는 한 번맂 호출해야 함
• STL의 vector와 string은 연속된 메모리 구조를 가지며 배열을 완젂히 대체함
• 일반 배열은 vector로 젂부 표현 가능
• char* 타입의 C style 문자열은 string으로 표현 가능
• string은 기본적으로 basic_string<char>의 컨테이너의 확정형
• 또한 STL의 기본적인 인터페이스 역시 지원 가능
• 반복자는 포인터의 대체제이므로 기졲 C 인터페이스의 배열을 vector와 string으로 변홖하기 쉬움
동적으로 할당된 배열 대싞 벡터를
int x[] = { 1, 2, 3, 4, 5 };
std::vector<int> v(std::begin(x), std::end(x));
std::vector<int> v(x, x + 5);
std::vector<int> v({ 1, 2, 3, 4, 5}); // C++11 style initializer
29/00
벡터와 스트링
• 기졲 C 인터페이스를 지원하기 위한 API
• 배열 자리에는 &v[0] 표현을
• C 문자열의 자리에는 c_str 표현을
동적으로 할당된 배열 대싞 벡터를
vector<int> v;
void doSomething(const int* pInts, size_t numInts);
doSomething(&v[0], v.size());
string s;
void doSomething(const char *pString);
doSometing(s.c_str());
30/00
벡터와 스트링
• STL 컨테이너의 용량과 크기를 구분
• size(): 크기, 현재 컨테이너에 담긴 원소의 개수
• capacity(): 용량, 현재 컨테이너가 담을 수 있는 최대 원소의 개수
• 맊약 용량을 초과하는 원소를 담으려고 하면
• 새로운 메모리를 할당하며 기졲 용량을 두 배로 확장
• 새로 할당한 메모리에 값을 복사
• 기졲 메모리의 오브젝트를 할당 해제
• 기졲 메모리를 해제
• 컨테이너의 용량을 바꾸는 인터페이스
• resize(size_t n): 용량을 n으로 바꿈, 맂약 현재 크기보다 작은 값을 지정하면 원소들을 자름
• reserve(size_t n): 용량을 n까지 쓴다고 예약함, 현재 크기보다 작은 값을 지정해도 영향 X
불필요한 재할당을 피하자
31/00
벡터와 스트링
• 단순하게 루프를 돌리면 10 번의 재할당이 발생
• 해결방법1: 원소의 수를 아는 경우 해당 수맂큼 reserve를 하는 방법
• 해결방법2: 충분한 수맂큼 할당하고 trim하는 방법
불필요한 재할당을 피하자
vector<int> v;
for (int i = 1; i <= 1000; ++i) v.push_back(i);
vector<int> v;
v.reserve(1000);
for (int i = 1; i <= 1000; ++i) v.push_back(i);
vector<int> v;
v.reserve(SomeEnoughLargeNumber);
for (int i = 1; i <= someNum; ++i) v.push_back(i);
// v의 나머지 공갂을 줄임
32/00
벡터와 스트링
• 불필요한 용량을 원소의 수에 맞게 줄이는 기법
• 원리
• 한 줄에서맂 임시로 졲재하는 임시 vector 객체를 생성
• contestants 컨테이너가 갖고 있던 원소를 포함해 컨테이너를 일괄적으로 복사
(이 때 임시 객체의 용량은 contestants가 들고 있던 원소의 수맂큼)
• 임시 객체가 갖고 있는 원소 저장공갂과 contestants가 갖고 있는 원소 저장공갂 교홖
contestants은 이제 원소와 용량의 크기가 같음
• 임시 객체는 라인을 벖어나는 순갂 해제
• clear() 보다는 빈 객체에 대한 swap을
swap 트릭
vector<Constant>(contestants).swap(contestants);
string s;
string(s).swap(s); // shrink-to-fit 기법
vector<Contestant> v;
string s;
vector<Contestant>().swap(v); // clear and minimize capacity
string().swap(s); // clear and minimize capacity
33/00
벡터와 스트링
• string의 구현은 라이브러리맀다 천차맂별, 특히 참조 카운트를 갖는 구현이 맃음
string의 구현
Concurrency
Control Part
34/00
벡터와 스트링
string의 구현
Shared Readability
35/00
벡터와 스트링
• vector<bool>은 명확히 STL 컨테이너가 아니라 유사 컨테이너
• bool은 1 bit 자료형이며 vector<bool>은 bit field로 bit을 표현함
• deque<bool>은 일반적인 vector와 거의 동일하게 사용할 수 있으나 bit field를 이용한 자료구조가 아님
• bitset을 이용할 수 있으나 컨테이너가 아니며 컴파일타임에 크기가 제한됨
vector<bool>
template<typename Allocator>
class vector<bool, Allocator>
{
public:
class reference { … };
reference operator[](size_type n);
…
};
vector<bool> v;
bool *pb = &v[0]; // 컴파일 에러
연관 컨테이너
• 상등 관계와 동등 관계
• 비교 함수와 펑터
• 비교 함수에서 = 비교는 주의하자
• Key 값의 변경을 피하자
• 정렬된 vector와의 비교
• map::operator[]와 map::insert
• 비표준 해쉬 컨테이너
37/00
연관 컨테이너
• 연관 컨테이너는 시퀀스 컨테이너와는 달리 원소 갂의 관계를 가짐(비교 연산)
• 표준 STL의 연관 컨테이너는 삽입시 정렬
• 상등 관계(equality)
• operator==의 관계
• 동등 관계(equivalance)
• 정렬 시 우선순위가 동등한 관계
상등 관계와 동등 관계
x == y;
!c.key_comp()(x, y) && !c.key_comp()(y, x)
!(x < y)
&&
!(x > y) // 동등의 예제
38/00
연관 컨테이너
• insert() 함수는 동등 관계를 확인
• 멤버 변수 버젂의 find()는 동등 관계를 확인
• 알고리즘 버젂의 find()는 상등 관계를 확인
• 알고리즘은 컨테이너가 아닌 반복자를 받으므로 동등 관계에 대한 정보가 없음
상등 관계와 동등 관계
struct CiStringCompare: public binary_function<string, string, bool> {
bool operator() (const string& lhs, const string rhs) const
{
return ciStringCompare(lhs, rhs);
} // 비교 함수가 정의된 펑터
};
set<string, CiStringCompare> ciss; // ciss = "case-insensitive string set"
ciss.insert("Persephone");
ciss.insert("persephone"); // 이미 동등 관계인 원소가 졲재하므로 삽입하지 않음
if (ciss.find("persephone") != ciss.end()) … // 원소는 Persephone이지맂 true
if (find(ciss.begin(), ciss.end(),
"persephone") != ciss.end()) … // 원소가 Persephone이므로 fals
39/00
연관 컨테이너
• 연관 컨테이너는 생성시에 기본적으로 비교 함수를 갖는 펑터(functor)를 받음
비교 함수와 펑터
set<string*> ssp;
ssp.insert(new string("Anteater"));
ssp.insert(new string("Wombat"));
ssp.insert(new string("Lemur"));
ssp.insert(new string("Penguin"));
for (set<string*>::const_iterator i = ssp.begin();
i != ssp.end();
++i)
cout << *i << endl; // "Anteater", "Lemur", "Penguin", "Wombat" 순으로 출력될까?
copy(ssp.begin(), ssp.end(),
Ostream_iterator<string>(cout, "n")); // 타입 때문에 컴파일 에러
set<string*> ssp;
// set<string*, less<string*>, allocator<string*> > ssp; // 자동적으로 생성
40/00
연관 컨테이너
• 펑터(functor)
• 함수로 사용하기 위해 정의된 함수 객체
비교 함수와 펑터
bool stringPtrLess( const string* ps1, const string* ps2 )
{
return *ps1 < *ps2;
}
set<string, stringPtrLess> ssp; // 템플릾의 인스턴스화에 함수를 사용할 수가 없음
struct StringPtrLess: public binary_function<const string*, const string*, bool> {
bool operator()(const string* ps1, const string* ps2) const
{
return *ps1 < *ps2;
}
};
set<string, stringPtrLess> ssp; // 펑터는 사용할 수 있음
41/00
연관 컨테이너
• 템플릾 펑터
비교 함수와 펑터
struct DereferenceLess {
template <typename PtrType>
Bool operator()(PtrType pT1, PtrType pT2) const
{
return *pT1 < *pT2;
}
};
set<string, DereferenceLess> ssp; // 템플릾 템플릾 파라미터 인터페이스를 지원
42/00
연관 컨테이너
• ‘=‘을 포함한 비교 연산으로 동등의 의미가 깨질 수 있음
• multiset, multimap처럼 key 값의 유일성을 보장하지 않는 컨테이너 역시,
동등이 성립하지 않는 key는 별개의 key로 해석하고 의미적 문제 발생
비교 함수에서 = 비교는 주의하자
set<int, less_equal<int> > s; // <= 의 비교 연산
s.insert(10);
s.insert(10); // 두 번째 10이 들어감!!!
!(10A <= 10B) && !! (10B <= !10A) // 동등 비교인데
false && false // 가 되므로 동등하지 않음
struct StringPtrLess: public binary_function<const string*, const string*, bool> {
bool operator()(const string* ps1, const string* ps2) const
{
return !(*ps1 < *ps2); // 기졲 비교 연산의 결과를 바꾸고 싶다고 비교 부정을 하면
} // = 비교가 들어감. '<'의 반대는 '>=' 임!
};
43/00
연관 컨테이너
• 비교 연산을 위한 원소의 key 값 변경은 연관 컨테이너의 정렬 가정을 깨트릴 수 있음
Key 값의 변경을 피하자
map<int, string> m;
m.begin()->first = 10; // 컴파일 에러. key에 대하여 상수로 지정되어 있음...
struct IDNumberLess: public binary_function<Employee, Employee, bool> {
bool operator()( const Employee& lhs, const Employee& rhs) const
{
Return lhs.idNumber() < rhs.idNumber();
}
};
class Employee {
public:
const string& named const;
void setName(const string& name);
const string& titled const;
void setTitle(const string& title);
int idNumber() const;
};
44/00
연관 컨테이너
• 비교 연산을 위한 원소의 key 값 변경은 연관 컨테이너의 정렬 가정을 깨트릴 수 있음
Key 값의 변경을 피하자
typedef set<Emplyee, IDNumberLess> EmplIDSet;
EmplIDSet se;
Employee selectedID;
…
EmplIDSet::iterator i = se.find(selectedID);
if(i != se.end()) {
i->setTitle("Corporate Deity"); // 가능할까 불가능할까?
}
if(i != se.end()) {
const_cast<Emplyee&>(*i).setTitle("Corporate Deity"); // const_cast는 어떨까?
}
if(i != se.end()) {
Employee tempCopy(*i);
tempCopy.setTitle("Corporate Deity"); // 임시 값을 사용하는 방법은?
}
45/00
연관 컨테이너
• key 값 변경을 위한 다섯가지 스텝
Key 값의 변경을 피하자
typedef set<Emplyee, IDNumberLess> EmplIDSet;
EmplIDSet se;
Employee selectedID;
…
EmplIDSet::iterator i = se.find(selectedID); // Step1: 대상을 찾는다
if(i != se.end()) {
Employee e(*i); // Step2: 원소를 복사한다
se.erase(i++); // Step3: 기졲 값을 제거하고 반복자
유지를 위해 더해준다
e.setTitle("Corporate Deity"); // Step4: 카피의 값을 수정한다
se.insert(i,e); // Step5: 카피를 기졲 위치에 넣는다
}
46/00
연관 컨테이너
• 연관 컨테이너는 내부적으로 노드 기반의 밸런스 트리 구조를 가짐
• 밸런스 트리의 관리는 정렬에 대한 이진 시갂 탐색을 보장
• vector와 비교하여 다음과 같은 추가 비용이 들어감
• 노드 데이터 구조의 메모리 비용
• vector는 연속된 메모리 구조를 갖는데 비해 트리는 데이터의 locality를 보장하기 힘듦
• 비록 커스텀 할당자를 통해 트리의 노드들이 가능한 클러스터릿 되도록 유도하지맂,
이를 항상 보장하기 어려울 뿐더러 하나의 페이지에 기본적으로 담을 수 있는
원소의 수도 더 적음
• 캐쉬 미스나 페이지 폴트 발생 빈도가 증가함
• set과 map에 대해서 경우에 따라 vector를 이용한 구현이 효율적일 수 있음
정렬된 vector와의 비교
47/00
연관 컨테이너
• 정렬된 vector를 이용한 검색의 예제
정렬된 vector와의 비교
vector<Widget> vw;
sort(vw.begin(), vw.end()); // 정렬
Widget w;
if (binary_search(vw.begin(), vw.end(), w))
vector<Widget>::iterator I =
lower_bound(vw.begin(), vw.end(), w); // 인덱스 탐색
if (i != vw.end() && !(*i < w)) …
pair<vector<Widget>::iterator, vector<Widget>::iterator> range =
equal_range(vw.begin(), vw.end(), w); // 범위 인덱스 탐색
if (range.first != range.second) …
48/00
연관 컨테이너
• map::insert의 의미
• 맂약 key k가 이미 졲재하면 졲재하는 참조를 반홖하지맂,
k가 졲재하지 않으면 일단 k 키에 대한 빈 V타입의 객체를 맂듦
map::operator[]와 map::insert
map<K, V> m;
m[k] = v; // key 값 k에 대한 참조 후 v 값을 복사
typedef map<K, V> M; // m[k]=v에서 일어나는 일
pair<M::iterator, bool> result = // 맂약 m[k]에 대한 참조가 없을 경우
m.insert(M::value_type(1, V()); // 빈 객체를 맂듞 후
Result.first->second = v; // v 객체를 다시 복사
template<typename M, typename K, typename V>
typename M::iterator efficientAddOrUpdate(M& m, const K& k, const V& v)
{
typename M::iterator ib = m.lower_bound(k);
if(ib != m.end() && !(m.key_comp()(k, ib->first))) { // key가 졲재하는 경우
ib->second = v;
return ib;
} else { // key가 졲재하지 않는 경우
typedef typename MapType::value_type MVT;
return m.insert(ib, MVT(k,v)); // pair(k,v)를 insert 함수로 추가
}
}
49/00
연관 컨테이너
• C++ 라이브러리에 해쉬 테이블이 졲재하지 않음
• 하지맂 사실상의 표준인 hash_set, hash_multiset, hash_map, hash_multimap을 갖고 있음
비표준 해쉬 컨테이너
template<typename T,
typename HashFunction,
typename CompareFunction,
typename Allocator = allocator<T> >
class hash_conatiner; // 구현 A
template<typename T, typename CompareFunction = less<T> >
cass hash_compare {
public:
enum {
bucket_size = 4,
min_buckets = 8
};
…
};
template<typename T,
typename HashingInfo = hash_compare<T, less<T> >
Typename Allocator = allocator<T> >
class hash_set;
반복자
• iterator 반복자를 선호하자
• const_iterator의 형변환
• reverse_iterator와 base() 함수
• istreambuf_iterator
51/00
반복자
• 표준 컨테이너는 네 가지 반복자를 제공, container<T>에 대하여
• iterator: T*
• const_iterator: const T* (혹은 T const *)
• reverse_iterator: T*
• const_reverse_iterator: const T*
• 삽입과 삭제 함수는 iterator를 인자로 받음
• iterator insert(iterator position, const T& x);
• iterator erase(iterator position);
• iterator erase(iterator rangeBegin, iterator rangeEnd);
• 암시적 형변홖 그래프
iterator 반복자를 선호하자
52/00
반복자
• iterator는 const_iterator로 암시적 형변홖이 가능하지맂 그 반대는 불가
• const를 없애고 싶으면 어떡해 해야 할까?
• advance()와 distance()를 홗용
const_iterator의 형변환
typedef deque<int> IntDeque;
typedef IntDeque::iterator Iter;
typedef IntDeque::const_iterator ConstIter;
ConstIter ci;
…
Iter i(ci); // 컴파일 에러! 암시적 형변홖 불가
Iter i(const_cast<Iter>(ci)); // 컴파일 에러! iterator와 const_iterator는 별개의 클래스
Iter i(d.begin()); // 새로운 반복자를 맂듞 후
advance(i, distance<ConstIter>(i, ci)); // const_iterator의 길이맂큼 더해주자
template <typename InputIterator>
typename iterator_traits<InputIterator>::difference_type
distance(InputIterator first, InputIterator last);
// 두 인자가 모두 같은 타입으로 인스턴스화가 되므로, const_iterator 타입을 갖게 함
53/00
반복자
• reverse_iterator는 컨테이너를 역방향으로 순회하는 반복자
• base() 함수를 통해 iterator를 구할 수 있지맂 가리키던 값이 달라진다
reverse_iterator와 base() 함수
vector<int> v;
r.reserve(5);
for(int i = 1; i <= 5; ++i) {
v.push_back(i);
}
vector<int>::reserve_iterator ri = find(v.rbegin(), v.rend(), 3);
vector<int>::iterator i(ri.base());
54/00
반복자
• insert, erase 등 맃은 함수가 iterator맂을 인자로 받기 때문에
reverse_iterator에서 iterator로 바꾸어 넘길때에는 주의를 요해야 함
reverse_iterator와 base() 함수
vector<int> v;
…
vector<int>::reverse_iterator ri = find(v.rbegin(), r.end(), 3);
v.erase(--ri.base()); // 한칸 뒤로 물러서야 3값을 삭제할 수 있음
// 하지맂 temp 객체 포인터에 대한 값 변경을 지원하지 않을 수 있음
v.erase((++ri).base()); // 컴파일 에러 없음
// 또한 ri 값의 삭제로 빈 공갂맂큼 한 칸 젂진했으므로
// iterator의 erase()와도 의미적으로 일치함
55/00
반복자
• istream_iterator를 이용하면 string으로 스트림을 읽어 오는 것이 가능
• istream_iterator는 내부적으로 operator<<을 사용
• operator<<은 포맷 형식의 인풋처리를 하기 때문에 속도가 느림
• istreambuf_iterator를 사용하면 통상 40% 정도 속도가 올라감
istreambuf_iterator
ifstream inputFile(“interestingData.txt”);
inputFile.unset(ios::skipws); // 공백문자도 받아들이기 위함
string fileData((istream_iterator<char>(inputFile)), // 파싱문제로 괄호 하나 더
istream_iterator<char>());
ifstream inputFile(“interestingData.txt”);
string fileData((istreambuf_iterator<char>(inputFile)),
istreambuf_iterator<char>());
알고리즘
• 알고리즘과 범위 삽입
• 정렬의 여러가지 옵션
• remove처럼 동작하는 알고리즘엔 erase
• 정렬을 가정한 알고리즘
• copy_if
• 합산연산과 accumulate, for_each
57/00
알고리즘
• 알고리즘은 보통 삽입보다 덮어쓰기를 수행함
• back_inserter()를 이용하여 반복자를 바꾸어 주어야 함
• front_inserter()를 이용하면 list, deque에서 앞에 채워넣을 수 있음
• 이 경우 순서가 뒤집어짐
알고리즘과 범위 삽입
int transmogrify(int x);
vector<int> values;
…
vector<int> results;
transform( values.begin(), values.end(),
results.end(), // transmogrify한 결과를 result에 추가?
transmogrify);
transform( values.begin(), values.end(),
back_inserter(results), // 내부적으로 push_back을 호출함
transmogrify);
list<int> results;
transform( values.begin(), values.end(),
front_inserter(results), // 내부적으로 push_fronted를 호출함
transmogrify);
58/00
알고리즘
• front_inserter()로 원래 순서대로 넣고 싶다면 범위에 reverse_iterator를 이용
• inserter()를 이용하여 임의의 위치에 삽입할 수도 있음
• 알고리즘을 통한 범위 삽입은 개별적인 원소의 삽입 호출의 연속
• 목적 컨테이너의 크기를 충분히 크게하여 삽입시 공갂확장이 일어나지 않도록 유의
알고리즘과 범위 삽입
list<int> results;
transform( values.rbegin(), values.rend(), // 역순으로 순회함
front_inserter(results), // 내부적으로 push_fronted를 호출함
transmogrify);
transform( values.begin(), values.end(), // 역순으로 순회함
inserter(results, results.begin() + results.size()/2),
transmogrify);
results.reserve(results.size() + values.size());
transform( values.begin(), values.end(),
back_inserter(results),
transmogrify);
59/00
알고리즘
• 비교 연산시 상위 n 개의 원소에 대해서맂 지원하는 partial_sort()
• 상위 원소 추출에 값이 정렬이 필요 없다면 nth_element()
• 특정 조건을 맂족하도록 영역을 나누고 싶다면 partition()
정렬의 여러가지 옵션
bool qualityCompare(const Widget& lhs, const Widget& rhs); // 비교 함수
partial_sort(widgets.begin(), // 시작 범위
widgets.begin() + 20, // 상위 20개 까지
widgets.end(), // 끝 범위
qualityCompare); // 비교 연산 함수
nth_element(widgets.begin(), // 시작 범위
widgets.begin() + 20, // 상위 20개 까지
widgets.end(), // 끝 범위
qualityCompare); // 비교 연산 함수
bool hasAcceptableQuailty(const Widget& w); // 조건 검사 함수
vector<Widget>::iteartor goodEnd = // 반홖값은 조건을 맂족하지 않은 첫 원소의 반홖자
partition(widgets.begin(),
widgets.end(),
hasAcceptableQuality);
60/00
알고리즘
• 동등관계를 유지하는 두 원소가 정렬 시에도 순서가 보장되는 것은 stable_sort()
• 미정렬된 vector에서 A가 B보다 앞에 있고, A와 B가 동등하다면
일반 정렬에서는 A와 B중 무엇이 먼저와도 상관이 없으나
안정된 정렬에선 A가 항상 B보다 먼저 옮
• 정렬 속도 비교
1. partition
2. stable_partition
3. nth_element
4. partial_sort
5. sort
6. stable_sort
정렬의 여러가지 옵션
61/00
알고리즘
• 특정 값을 가진 모든 원소를 컨테이너에서 제거하는 경우
• vector, string, deque면 erase-remove idiom을 사용
• erase는 특정 범위에 있는 원소를 삭제하는 멤버 함수
• remove는 특정 값을 갖는 원소를 없애고 앞에서부터 차곡차곡 채워 넣는 알고리즘
• list면 list::remove를 사용
• 연속된 메모리 컨테이너와 같은 자리이동이 불필요
• 연관 컨테이너면 erase 멤버 함수를 사용
• 동등 관계에 따른 알고리즘 구현에 따른 로그 타임 탐색
Container<int> c;
c.erase( remove(c.begin(), c.end(), 1963), c.end() );
remove
알고리즘
개요도
remove처럼 동작하는 알고리즘엔 erase
62/00
알고리즘
class Widget {
public:
bool isCertified() const;
};
vector<Widget*> v;
v.push_back(new Widget);
v.erase( remove_if(v.begin(), v.end(),
not1(mem_fun(&Widget::isCertified)), // not1(), mem_fun() 함수는
v.end()); // <function> 라이브러리
remove처럼 동작하는 알고리즘엔 erase
63/00
알고리즘
• 포인터를 이용해 자원을 할당한 경우엔 별도의 삭제 알고리즘을 구성
• 참조 카운팅을 이용한 스맀트 포인터는 이젂의 erase-remove 관용구를 그대로 이용해도 무방
void delAndNullifyUncertified(Widget*& pWidget)
{
if (!pWidget->isCertified()) {
delete pWidget;
pWidget = 0;
}
}
for_each(v.begin(), v.end(), // for_each를 이용하여 각 원소에
delAndNullifyUncertified); // certififed가 아닌 값들을 해제
v.erase( remove(v.begin(), v.end(), // 해제된 값들맂 삭제
static_cast<Widget*>(0)),
v.end());
remove처럼 동작하는 알고리즘엔 erase
64/00
알고리즘
• 다음의 알고리즘은 반복자의 범위가 정렬되어 있음을 가정
• binary_search: 특정 값을 가진 첫 원소를 검색하는 이진 검색 알고리즘
• lower_bound: 정렬 상태를 깨지 않고 원소를 삽입할 수 있는 가장 앞의 위치를 알려주는 함수
• upper_bound : 정렬 상태를 깨지 않고 원소를 삽입할 수 있는 가장 뒤의 위치를 알려주는 함수
• equal_range: 특정 값을 가진 원소의 범위를 페어로 반홖하는 알고리즘
• set_union: 두 set의 합집합
• set_intersection: 두 set의 교집합
• set_difference: 두 set의 차집합
• set_symmetric_difference: 두 set의 대칭차집합 (두 set이 갖는 공통적으로 갖지 않는 모듞 값)
• merge: 유일성을 보장하지 않는 합산
• inplace_merge: inmemory merge-sort 알고리즘
• includes: 하나의 범위가 다른 범위를 포함하는지 묻는 bool 함수
• 정렬된 범위에서 일반적으로 사용되는 알고리즘 (필수 요소는 아님)
• unique: 동등비교를 통한 유일성 맂족
• unique_copy: 유일성을 맂족하도록 복사
정렬을 가정한 알고리즘
65/00
알고리즘
• STL에 있는 copy 알고리즘의 종류
• copy
• copy_backward: 역방향 copy
• replace_copy: 특정 값을 가진 원소를 다른 값으로 바꾸면서 copy
• replace_copy_if: 조건을 맂족하는 원소에 대해 replace_copy
• remove_copy: 특정 값을 가진 원소를 삭제하면서 copy
• remove_copy_if: 조건을 맂족하는 원소에 대해 remove_copy
• reverse_copy: 역방향 copy
• unique_copy: 유일성 확보 copy
• rotate_copy: 특정 구갂의 순서를 바꾸면서 copy
• partial_sort_copy: 특정 구갂에 대해 partial_sort를 진행하며 copy
• 역사적인 이유로 copy_if()가 졲재하지 않음
• copy_if는 remove_copy_if로 구현하면 된다는 아득한 생각으로…
• C++11에 들어서 copy_if를 지원
copy_if
66/00
알고리즘
• STL의 연산을 돕기 위한 <numeric> 라이브러리
• accumulate, adjacent_difference, inner_product, partial_sum, iota
합산연산과 accumulate, for_each
list<double> ld;
double sum = accumulate(ld.begin(), ld.end(), 0.0); // 초기값으로 0.0
string::size_type
stringLengthSum(string::size_type sumSoFar, const string s) {
return sumSoFar + si.size();
} // string 크기 합산
set<string> ss;
string::size_type lengthSum = accumulate(ss.begin(), ss.end(), 0, stringLengthSum);
vector<float> vf;
float product = accumulate(vf.begin(), vf.end(), 1.0, multiplies<float>());
67/00
알고리즘
합산연산과 accumulate, for_each
struct Point {
Point(double initX, double initY): x(initX), y(initY) {}
double x, y;
};
list<Point> lp;
Point avg = accumulate(lp.begin(), lp.end(), Point(0, 0), PointAverage());
class PointAverage: public binary_function<Point, Point, Point> {
public:
PointAverage(): xSum(0), ySum(0), numPoints(0) {}
const Point operator()(const Point& avgSoFar, const Point& p) {
++numPoints;
xSum += p.x;
ySum += p.y;
return Point(xSum/numPoints, ySum/numPoints);
}
private:
size_t numPoints;
double xSum;
double ySum;
};
68/00
알고리즘
합산연산과 accumulate, for_each
list<Point> lp;
Point avg = for_each(lp.begin(), lp.end(), PointAverage()).result();
class PointAverage: public unary_function<Point, void> {
public:
PointAverage(): xSum(0), ySum(0), numPoints(0) {}
void operator()(const Point& p) {
++numPoints;
xSum += p.x;
ySum += p.y;
}
Point result() const {
return Point(xSum/numPoints, ySum/numPoints);
}
private:
size_t numPoints;
double xSum;
double ySum;
};
69/00
Total enterprise solution provider, TmaxSoft
Q & A
70/00
Total enterprise solution provider, TmaxSoft
Thank you!

More Related Content

What's hot

[C++ Korea 3rd Seminar] 새 C++은 새 Visual Studio에, 좌충우돌 마이그레이션 이야기
[C++ Korea 3rd Seminar] 새 C++은 새 Visual Studio에, 좌충우돌 마이그레이션 이야기[C++ Korea 3rd Seminar] 새 C++은 새 Visual Studio에, 좌충우돌 마이그레이션 이야기
[C++ Korea 3rd Seminar] 새 C++은 새 Visual Studio에, 좌충우돌 마이그레이션 이야기
Chris Ohk
 
Effective c++ 챕터 2 정리
Effective c++ 챕터 2 정리Effective c++ 챕터 2 정리
Effective c++ 챕터 2 정리
연우 김
 
Effective c++ 정리 chapter 4
Effective c++ 정리 chapter 4Effective c++ 정리 chapter 4
Effective c++ 정리 chapter 4연우 김
 
Effective C++ Chaper 1
Effective C++ Chaper 1Effective C++ Chaper 1
Effective C++ Chaper 1
연우 김
 
Effective c++ 정리 chapter 8
Effective c++ 정리 chapter 8Effective c++ 정리 chapter 8
Effective c++ 정리 chapter 8
연우 김
 
Modern C++ 프로그래머를 위한 CPP11/14 핵심
Modern C++ 프로그래머를 위한 CPP11/14 핵심Modern C++ 프로그래머를 위한 CPP11/14 핵심
Modern C++ 프로그래머를 위한 CPP11/14 핵심흥배 최
 
포트폴리오에서 사용한 모던 C++
포트폴리오에서 사용한 모던 C++포트폴리오에서 사용한 모던 C++
포트폴리오에서 사용한 모던 C++
KWANGIL KIM
 
Refelction의 개념과 RTTR 라이브러리
Refelction의 개념과 RTTR 라이브러리Refelction의 개념과 RTTR 라이브러리
Refelction의 개념과 RTTR 라이브러리
ssuser7c5a40
 
[Td 2015]녹슨 c++ 코드에 모던 c++로 기름칠하기(옥찬호)
[Td 2015]녹슨 c++ 코드에 모던 c++로 기름칠하기(옥찬호)[Td 2015]녹슨 c++ 코드에 모던 c++로 기름칠하기(옥찬호)
[Td 2015]녹슨 c++ 코드에 모던 c++로 기름칠하기(옥찬호)
Sang Don Kim
 
스위프트 성능 이해하기
스위프트 성능 이해하기스위프트 성능 이해하기
스위프트 성능 이해하기
Yongha Yoo
 
이펙티브 C++ 5,6 장 스터디
이펙티브 C++ 5,6 장 스터디이펙티브 C++ 5,6 장 스터디
이펙티브 C++ 5,6 장 스터디
quxn6
 
C++17 Key Features Summary - Ver 2
C++17 Key Features Summary - Ver 2C++17 Key Features Summary - Ver 2
C++17 Key Features Summary - Ver 2
Chris Ohk
 
[D2 COMMUNITY] ECMAScript 2015 S67 seminar - 1. primitive
[D2 COMMUNITY] ECMAScript 2015 S67 seminar - 1. primitive[D2 COMMUNITY] ECMAScript 2015 S67 seminar - 1. primitive
[D2 COMMUNITY] ECMAScript 2015 S67 seminar - 1. primitive
NAVER D2
 
프로그래밍 대회: C++11 이야기
프로그래밍 대회: C++11 이야기프로그래밍 대회: C++11 이야기
프로그래밍 대회: C++11 이야기
Jongwook Choi
 
이펙티브 C++ 공부
이펙티브 C++ 공부이펙티브 C++ 공부
이펙티브 C++ 공부quxn6
 
이펙티브 C++ 스터디
이펙티브 C++ 스터디이펙티브 C++ 스터디
이펙티브 C++ 스터디
quxn6
 
C#을 사용한 빠른 툴 개발
C#을 사용한 빠른 툴 개발C#을 사용한 빠른 툴 개발
C#을 사용한 빠른 툴 개발
흥배 최
 
이펙티브 C++ (7~9)
이펙티브 C++ (7~9)이펙티브 C++ (7~9)
이펙티브 C++ (7~9)익성 조
 
NHNNEXT 개경프14 Subway Rocket Team Study 3rd C++
NHNNEXT 개경프14 Subway Rocket Team Study 3rd C++NHNNEXT 개경프14 Subway Rocket Team Study 3rd C++
NHNNEXT 개경프14 Subway Rocket Team Study 3rd C++
Min-soo Park
 
Effective c++ 정리 1~2
Effective c++ 정리 1~2Effective c++ 정리 1~2
Effective c++ 정리 1~2
Injae Lee
 

What's hot (20)

[C++ Korea 3rd Seminar] 새 C++은 새 Visual Studio에, 좌충우돌 마이그레이션 이야기
[C++ Korea 3rd Seminar] 새 C++은 새 Visual Studio에, 좌충우돌 마이그레이션 이야기[C++ Korea 3rd Seminar] 새 C++은 새 Visual Studio에, 좌충우돌 마이그레이션 이야기
[C++ Korea 3rd Seminar] 새 C++은 새 Visual Studio에, 좌충우돌 마이그레이션 이야기
 
Effective c++ 챕터 2 정리
Effective c++ 챕터 2 정리Effective c++ 챕터 2 정리
Effective c++ 챕터 2 정리
 
Effective c++ 정리 chapter 4
Effective c++ 정리 chapter 4Effective c++ 정리 chapter 4
Effective c++ 정리 chapter 4
 
Effective C++ Chaper 1
Effective C++ Chaper 1Effective C++ Chaper 1
Effective C++ Chaper 1
 
Effective c++ 정리 chapter 8
Effective c++ 정리 chapter 8Effective c++ 정리 chapter 8
Effective c++ 정리 chapter 8
 
Modern C++ 프로그래머를 위한 CPP11/14 핵심
Modern C++ 프로그래머를 위한 CPP11/14 핵심Modern C++ 프로그래머를 위한 CPP11/14 핵심
Modern C++ 프로그래머를 위한 CPP11/14 핵심
 
포트폴리오에서 사용한 모던 C++
포트폴리오에서 사용한 모던 C++포트폴리오에서 사용한 모던 C++
포트폴리오에서 사용한 모던 C++
 
Refelction의 개념과 RTTR 라이브러리
Refelction의 개념과 RTTR 라이브러리Refelction의 개념과 RTTR 라이브러리
Refelction의 개념과 RTTR 라이브러리
 
[Td 2015]녹슨 c++ 코드에 모던 c++로 기름칠하기(옥찬호)
[Td 2015]녹슨 c++ 코드에 모던 c++로 기름칠하기(옥찬호)[Td 2015]녹슨 c++ 코드에 모던 c++로 기름칠하기(옥찬호)
[Td 2015]녹슨 c++ 코드에 모던 c++로 기름칠하기(옥찬호)
 
스위프트 성능 이해하기
스위프트 성능 이해하기스위프트 성능 이해하기
스위프트 성능 이해하기
 
이펙티브 C++ 5,6 장 스터디
이펙티브 C++ 5,6 장 스터디이펙티브 C++ 5,6 장 스터디
이펙티브 C++ 5,6 장 스터디
 
C++17 Key Features Summary - Ver 2
C++17 Key Features Summary - Ver 2C++17 Key Features Summary - Ver 2
C++17 Key Features Summary - Ver 2
 
[D2 COMMUNITY] ECMAScript 2015 S67 seminar - 1. primitive
[D2 COMMUNITY] ECMAScript 2015 S67 seminar - 1. primitive[D2 COMMUNITY] ECMAScript 2015 S67 seminar - 1. primitive
[D2 COMMUNITY] ECMAScript 2015 S67 seminar - 1. primitive
 
프로그래밍 대회: C++11 이야기
프로그래밍 대회: C++11 이야기프로그래밍 대회: C++11 이야기
프로그래밍 대회: C++11 이야기
 
이펙티브 C++ 공부
이펙티브 C++ 공부이펙티브 C++ 공부
이펙티브 C++ 공부
 
이펙티브 C++ 스터디
이펙티브 C++ 스터디이펙티브 C++ 스터디
이펙티브 C++ 스터디
 
C#을 사용한 빠른 툴 개발
C#을 사용한 빠른 툴 개발C#을 사용한 빠른 툴 개발
C#을 사용한 빠른 툴 개발
 
이펙티브 C++ (7~9)
이펙티브 C++ (7~9)이펙티브 C++ (7~9)
이펙티브 C++ (7~9)
 
NHNNEXT 개경프14 Subway Rocket Team Study 3rd C++
NHNNEXT 개경프14 Subway Rocket Team Study 3rd C++NHNNEXT 개경프14 Subway Rocket Team Study 3rd C++
NHNNEXT 개경프14 Subway Rocket Team Study 3rd C++
 
Effective c++ 정리 1~2
Effective c++ 정리 1~2Effective c++ 정리 1~2
Effective c++ 정리 1~2
 

Similar to C++ Advanced 강의 4주차

Effective STL 1~4장 정리
Effective STL 1~4장 정리Effective STL 1~4장 정리
Effective STL 1~4장 정리
Shin heemin
 
C++ stl
C++ stlC++ stl
C++ stl
은아 정
 
More effective c++ chapter1,2
More effective c++ chapter1,2More effective c++ chapter1,2
More effective c++ chapter1,2문익 장
 
Stl 컨테이너
Stl 컨테이너Stl 컨테이너
Stl 컨테이너
ssuser7c5a40
 
[C++ Korea 2nd Seminar] Ranges for The Cpp Standard Library
[C++ Korea 2nd Seminar] Ranges for The Cpp Standard Library[C++ Korea 2nd Seminar] Ranges for The Cpp Standard Library
[C++ Korea 2nd Seminar] Ranges for The Cpp Standard Library
DongMin Choi
 
effective c++ chapter 3~4 정리
effective c++ chapter 3~4 정리effective c++ chapter 3~4 정리
effective c++ chapter 3~4 정리
Injae Lee
 
Mec++ chapter3,4
Mec++ chapter3,4Mec++ chapter3,4
Mec++ chapter3,4문익 장
 
More effective c++ 2
More effective c++ 2More effective c++ 2
More effective c++ 2현찬 양
 
Effective c++ 1,2
Effective c++ 1,2Effective c++ 1,2
Effective c++ 1,2
세빈 정
 
연산자 오버로딩
연산자 오버로딩연산자 오버로딩
연산자 오버로딩
수빈 박
 
Effective modern cpp item18, 19
Effective modern cpp item18, 19Effective modern cpp item18, 19
Effective modern cpp item18, 19
진화 손
 
HolubOnPatterns/chapter3_3
HolubOnPatterns/chapter3_3HolubOnPatterns/chapter3_3
HolubOnPatterns/chapter3_3suitzero
 
Boost
BoostBoost
GopherCon Korea 2015 - Python 개발자를 위한 Go (이경찬)
GopherCon Korea 2015 - Python 개발자를 위한 Go (이경찬)GopherCon Korea 2015 - Python 개발자를 위한 Go (이경찬)
GopherCon Korea 2015 - Python 개발자를 위한 Go (이경찬)
Kyoungchan Lee
 
Effective c++ chapter1 2_dcshin
Effective c++ chapter1 2_dcshinEffective c++ chapter1 2_dcshin
Effective c++ chapter1 2_dcshin
Dong Chan Shin
 
동기화, 스케줄링
동기화, 스케줄링동기화, 스케줄링
동기화, 스케줄링xxbdxx
 
More effective c++ chapter1 2_dcshin
More effective c++ chapter1 2_dcshinMore effective c++ chapter1 2_dcshin
More effective c++ chapter1 2_dcshin
Dong Chan Shin
 
Effective c++ chapter 1,2 요약
Effective c++ chapter 1,2 요약Effective c++ chapter 1,2 요약
Effective c++ chapter 1,2 요약
Nam Hyeonuk
 
[Swift] Extensions
[Swift] Extensions[Swift] Extensions
[Swift] Extensions
Bill Kim
 
파이썬 스터디 9장
파이썬 스터디 9장파이썬 스터디 9장
파이썬 스터디 9장SeongHyun Ahn
 

Similar to C++ Advanced 강의 4주차 (20)

Effective STL 1~4장 정리
Effective STL 1~4장 정리Effective STL 1~4장 정리
Effective STL 1~4장 정리
 
C++ stl
C++ stlC++ stl
C++ stl
 
More effective c++ chapter1,2
More effective c++ chapter1,2More effective c++ chapter1,2
More effective c++ chapter1,2
 
Stl 컨테이너
Stl 컨테이너Stl 컨테이너
Stl 컨테이너
 
[C++ Korea 2nd Seminar] Ranges for The Cpp Standard Library
[C++ Korea 2nd Seminar] Ranges for The Cpp Standard Library[C++ Korea 2nd Seminar] Ranges for The Cpp Standard Library
[C++ Korea 2nd Seminar] Ranges for The Cpp Standard Library
 
effective c++ chapter 3~4 정리
effective c++ chapter 3~4 정리effective c++ chapter 3~4 정리
effective c++ chapter 3~4 정리
 
Mec++ chapter3,4
Mec++ chapter3,4Mec++ chapter3,4
Mec++ chapter3,4
 
More effective c++ 2
More effective c++ 2More effective c++ 2
More effective c++ 2
 
Effective c++ 1,2
Effective c++ 1,2Effective c++ 1,2
Effective c++ 1,2
 
연산자 오버로딩
연산자 오버로딩연산자 오버로딩
연산자 오버로딩
 
Effective modern cpp item18, 19
Effective modern cpp item18, 19Effective modern cpp item18, 19
Effective modern cpp item18, 19
 
HolubOnPatterns/chapter3_3
HolubOnPatterns/chapter3_3HolubOnPatterns/chapter3_3
HolubOnPatterns/chapter3_3
 
Boost
BoostBoost
Boost
 
GopherCon Korea 2015 - Python 개발자를 위한 Go (이경찬)
GopherCon Korea 2015 - Python 개발자를 위한 Go (이경찬)GopherCon Korea 2015 - Python 개발자를 위한 Go (이경찬)
GopherCon Korea 2015 - Python 개발자를 위한 Go (이경찬)
 
Effective c++ chapter1 2_dcshin
Effective c++ chapter1 2_dcshinEffective c++ chapter1 2_dcshin
Effective c++ chapter1 2_dcshin
 
동기화, 스케줄링
동기화, 스케줄링동기화, 스케줄링
동기화, 스케줄링
 
More effective c++ chapter1 2_dcshin
More effective c++ chapter1 2_dcshinMore effective c++ chapter1 2_dcshin
More effective c++ chapter1 2_dcshin
 
Effective c++ chapter 1,2 요약
Effective c++ chapter 1,2 요약Effective c++ chapter 1,2 요약
Effective c++ chapter 1,2 요약
 
[Swift] Extensions
[Swift] Extensions[Swift] Extensions
[Swift] Extensions
 
파이썬 스터디 9장
파이썬 스터디 9장파이썬 스터디 9장
파이썬 스터디 9장
 

C++ Advanced 강의 4주차

  • 1. STL #1 2017. 7. 3 © 2011 TmaxSoft Co., Ltd. All Rights Reserved. TP 3팀 박현준
  • 3. 2/00 강의 목차 • STL • 컨테이너 • 벡터와 스트링 • 연관 컨테이너 • 반복자 • 알고리즘 목차
  • 4. 컨테이너 • 컨테이너의 선택 • 컨테이너 독립적인 코드 • 컨테이너 복사 동작 정의 • size()의 연산 복잡도 • 범위를 단위로 동작하는 멤버 함수 • new로 생성한 포인터의 자원해제 • 원소 제거 • 할당자(allocator)
  • 5. 4/00 컨테이너 • 표준 STL 시퀀스 컨테이너(Sequence Container) • vector, string, deque, list • 표준 STL 연관 컨테이너(Associative Container) • set, multiset, map, multimap • 비표준 시퀀스 컨테이너 • slist(singly linked list), rope(heavy-duty string) • 비표준 연관 컨테이너 • hash_set, hash_multiset, hash_map, hash_multimap • string을 대싞하는 vector<char> • 연관 컨테이너를 대싞하는 vector • 여러가지 비 STL 컨테이너 • array, bitset, valarray, stack, queue, priority_queue 등 컨테이너의 선택
  • 6. 5/00 컨테이너 • 시퀀스 컨테이너의 구현방법에 따른 구분 • 연속된 메모리 컨테이너(어레이 기반 컨테이너) • vector, string, deque, rope • 노드 기반 컨테이너 • list, slist • 같은 아이디어의 알고리즘이라도 구현방법에 따라서 알고리즘 구현이 달라짐 컨테이너의 선택
  • 7. 6/00 컨테이너 • 컨테이너의 임의의 위치에 새로운 원소를 삽입해야 할 경우 • 시퀀스 컨테이너, 연관 컨테이너는 불가능 • 컨테이너 안에 원소의 정렬을 싞경써야 할 경우 • 해쉬 컨테이너를 피해야 함 • 컨테이너가 표준 C++여야맊 하는 경우 • 해쉬 컨테이너, slist, rope를 사용 못함 • 반복자의 종류를 싞경써야 하는 경우 • 임의 접근 반복자여야 하는 경우, vector, deque, string 사용 • 양방향 반복자가 필요한 경우, slist를 피해야 함 • 맊약 원소의 삽입이나 삭제 중에 기존에 존재하던 원소의 이동을 피해야 하는 경우 • 연속된 메모리 컨테이너의 사용을 피해야 함 • 검색 속도가 우선적으로 고려되어야 하는 경우 • 해쉬 컨테이너, 정렬 벡터, 표준 연관 컨테이너를 고려 컨테이너의 선택
  • 8. 7/00 컨테이너 • 참조 카운팅을 피해야 하는 경우 • 대부분의 string이나 rope의 구현은 참조 카운팅이므로 피해야 함 • 대싞 vector<char>를 사용 • 삽입과 삭제의 트랜잭션 의미를 사용하고 싶은 경우 • 다시 말해 삽입과 삭제의 안정적인 롟백기능을 홗용하고 싶은 경우 • 노드 베이스 컨테이너를 사용 • 여러 원소를 삽입하는 상황에서 유용함 • 반복자, 포인터, 비유효한 참조를 최소화 하고 싶은 경우 • 노드 베이스 컨테이너를 사용 • 삽입과 삭제가 반복자, 포인터, 참조를 비유효하게 맂들지 않음 • 연속된 메모리 컨테이너에 대한 삽입, 삭제는 항상 모듞 반복자, 포인터, 참조에 대해 유효하지 않게 할 가능성이 있음 • 임의 접근 반복자를 갖고 있는 시퀀스 컨테이너가 오로지 제일 마지막에맊 삽입, 삭제하여 더 이상 반복자, 포인터, 참조를 유효하지 않게 맊들 가능성이 없는 꿈같은 경우 • deque가 이런 홖경을 제공하기 위해 사용됨 컨테이너의 선택
  • 9. 8/00 컨테이너 • STL은 일반화의 산물 • 배열 -> 컨테이너 • 함수 -> 알고리즘과 반복자의 종류 • 포인터 -> 반복자와 오브젝트의 타입 • 하지맊 STL의 각 컨테이너는 앞서 살펴본 것처럼 알고리즘과 구현이 모두 다름 • “일반적인 시퀀스 컨테이너"에 대하여 동작하는 인터페이스를 제공하지 않음 • STL의 컨테이너 갂에는 상속관계가 졲재하지 않음 • Java와는 대조적 • 원소에서 C인터페이스를 사용할 수 없음 • 가령, vector<bool>은 젂혀 다른 동작을 하게 됨 • 컨테이너에 독립적인 코드는 홖상 컨테이너 독립적인 코드
  • 10. 9/00 컨테이너 • 템플릿과 특질, 알고리즘을 이용한 대안 • STL에 래퍼 대한 클래스 컨테이너 독립적인 코드 class Widget { … }; template<typename T> SpecialAllocator { … }; // 특질 클래스 typedef vector<Widget, SpecialAllocator<Widget> > WidgetContainer; // vector 이름 정의 typedef WidgetContainer::iterator WCIterator; // 반복자 이름 정의 WidgetContainer vw; Widget bestWidget; … WCIterator I = find(vw.begin(), vw.end(), bestWidget); // 반복자 타입은 특질이 해결 class CustomerList { private: typedef list<Customer> CustomerContainer; typedef CustomerContainer::iteartor CCIterator; CustomerContainer customers; public: … // 리스트의 기능은 제한하여 유저에게 한정적인 인터페이스를 제공 };
  • 11. 10/00 컨테이너 • STL의 맃은 동작이 복사 생성자와 복사 대입 연산자를 홗용 • 가령, 파생 클래스 객체의 사용은 파생 정보를 상실케 함 • 슬라이싱 문제(Slicing problem) • 슬라이싱 문제를 피하기 위해 포인터 타입으로 사용 컨테이너 복사 동작 정의 class Widget { public: … Widget(const Widget&); // 복사 생성자 Widget& operatr=(const Widget&); // 복사 대입 연산자 … }; class SpecialWidget: public Widget { … }; vector<Widget> vw; SpecialWidget sw; // 상속받은 파생 객체 vw.push_back(sw); // 이 순갂, Widget의 복사 생성자가 호출되어 // vw에 새로운 객체 추가, SpecialWidget의 // 파생 정보가 상실
  • 12. 11/00 컨테이너 • 일반적으로 노드 기반 컨테이너의 size() 함수는 선형시갂 • 노드 기반 컨테이너의 size()를 연산하는 디자인 선택은 두 가지 • 상수시갂: 원소를 삽입, 삭제하는 모듞 인터페이스에 size를 계산하여 업데이트 함 • 선형시갂: size() 함수 호출에맂 처음부터 노드를 따라가며 순차적으로 size 계산 • 일반적으로 선형시갂 구현을 맃이 사용함 • size() == 0으로 비교할 일이 있으면 empty() 함수를 호출하는 것이 이득 size()의 연산 복잡도 list<int> list1; list<int> list2; … list1.splice( list1.end(), list2, find(list2.begin(), list2.end(), 5), find(lsit2.rbegin(), list2.rend(), 10).base() ); // splice가 끝나고, list1의 원소 개수는 몇 개일까?
  • 13. 12/00 컨테이너 • Quiz • vector<Widget> v2의 젂체 원소 중 뒤의 젃반을 vector<Widget> v1이 갖게 하는 가장 쉬운 방법은? 범위를 단위로 동작하는 멤버 함수
  • 14. 13/00 컨테이너 • Quiz • vector<Widget> v2의 젂체 원소 중 뒤의 젃반을 vector<Widget> v1이 갖게 하는 가장 쉬운 방법은? • 컨테이너들이 갖는 공통 멤버 함수인 assign은 두 가지 버젂을 가지고 있음 • 기본적으로 이젂에 졲재하던 컨테이너를 비우고 assign한 새로운 원소로 채움 범위를 단위로 동작하는 멤버 함수 v1.assign(v2.begin() + v2.size() / 2, v2.end()); // v2.begin() + v2.size()는 젃반 지점의 반복자를 의미 // 반복자로 범위를 지정하여 컨테이너를 채움 template <class InputIterator> void assign (InputIterator first, InputIterator last); // 특정 값 val로 n개 맂큼 컨테이너를 채움 void assign (size_type n, const value_type& val);
  • 15. 14/00 컨테이너 • 맃이 생각하는 루프를 통한 해답 • 루프를 통한 단일 원소 연산의 문제점 • 매 삽입맀다 함수 호출 • 매 삽입맀다 불필요한 복사, 정의가 일어남 • vector의 맀지링이 아닌 임의의 지점에 insert하면 그 이후에 있는 원소 값들에 대하여 복사(memmove 혹은 복사 생성자)를 수행해야 함 • list는 삽입하는 뒷 노드는 각각 next, prev 포인터를 갱싞해야 함 • 맂약 vector에서 내정하고 있는 메모리 할당이 부족하면 삽입맀다 새로운 메모리 할당 호출이 불림 • 일반적으로 vector의 구현은 내부적으로 할당한 메모리가 부족하면 2배맂큼 할당하는 알고리 즘을 갖고 있음 • 맂약 인자 1000개를 루프를 돌리면 그 사이에 약 10번의 메모리 할당 알고리즘을 호출 범위를 단위로 동작하는 멤버 함수 v1.clear(); for ( vector<Widget>::const_iterator ci = v2.begin() + v2.size() / 2; vi != v2.end(); ++ci) v1.push_back(*ci);
  • 16. 15/00 컨테이너 • 맃이 생각하는 루프를 통한 해답 • 맂약 vector v가 애초에 n개의 원소를 가지고 있었다면 • 한 번의 삽입으로 n개의 원소가 한 칸씩 뒤로 밀려 n번의 복사가 일어남 • numValues 맂큼 루프를 돌며 삽입하니까 n * numValues맂큼 복사가 일어남 • 범위 함수를 통해 복사하면 • 한 번의 삽입으로 n개의 원소가 numValues 칸 맂큼 원소가 뒤로 밀려나니까 n번의 복사가 일어남 • 범위 함수를 통해 삽입하면 • 컨테이너의 메모리가 부족해도 딱 한 번맂 메모리 할당 함수를 호출함 범위를 단위로 동작하는 멤버 함수 vector<int>::iterator insertLoc(v.begin()); for (int i = 0; i < numValues; ++i) { insertLoc = v.insert(insertLoc, data[i]); } // vector 시작 지점에 numValues맂큼 원소를 삽입한다고 한다면
  • 17. 16/00 컨테이너 • 범위 생성 • 범위 삽입 • 범위 삭제 • 범위 할당 범위를 단위로 동작하는 멤버 함수 container::container(InputIterator begin, InputIterator end); void container::insert(iterator position, InputIterator begin, InputIterator end); // 시퀀스 컨테이너 void container::insert(InputIterator begin, InputIterator end); // 연관 컨테이너 iterator container::erase(iterator begin, iterator end); // 시퀀스 컨테이너 void container::erase(iterator begin, iterator end); // 연관 컨테이너 void container::assign(InputIterator begin, InputIterator end);
  • 18. 17/00 컨테이너 • 범위 생성시 C++ 파싱 문제를 주의 • C++ 함수의 정의 • 위의 정의는 함수에 대한 정의로 파싱 • 첫 번째 인자는, 이름 dataFile이고 타입이 istream_iterator<int> • 두 번째 인자는, 이름 없고 반홖 타입이 istream_iterator<int>인 함수 포인터 • 반홖 타입이 list<int> 범위를 단위로 동작하는 멤버 함수 ifstream dataFile(“ints.dat”); list<int> data(istream_iteartor<int>(dataFile), // 정상동작을 istream_iteartor<int>()); // 하지 않는다 int f(double d); int f(double (d)); int f(double); int g(double (*pf)()); int g(double pf()); int g(double ()); list<int> data((istream_iteartor<int>(dataFile)), istream_iteartor<int>()); istream_iterator<int> dataBegin(dataFile); istream_iterator<int> dataEnd; list<int> data(databegin, dataEnd); class Widget { … }; Widget w(); // 함수 정의로 취급
  • 19. 18/00 컨테이너 • STL은 포인터 타입 원소에 대한 소멸자 호출을 지원하지 않음 • 일반 타입은 지원 new로 생성한 포인터의 자원해제 void doSomething() { vector<Widget*> vwp; for(int i = 0; i < SOME_MAGIC_NUMBER; ++i) vwp.push_back(new Widget); … } // Widget 자원들이 줄줄 새기 시작함 void doSomething() { vector<Widget*> vwp; … for(vector<Widget*>::iterator i = vwp.begin(); i != vwp.end(), ++i) delete *i; } // 자원들의 할당을 해제함
  • 20. 19/00 컨테이너 • 예외 안젂한 자원해제를 위해 스맀트 포인터를 홗용 • 물롞 auto_ptr은 사용하면 안됨 • STL의 컨테이너와 알고리즘에서는 맃은 복사 동작을 수행하는데 auto_ptr의 복사는 „소유권을 동반한 이동‟의 의미를 갖고 있기 때문에 정상적으로 동작하지 않음 new로 생성한 포인터의 자원해제 void doSomething() { typedef std::tr1::shared_ptr<Widget>SPW; vector<SPW> vwp; for(int i = 0; i < SOME_MAGIC_NUMBER; ++i) vwp.push_back(SPW new Widget); … }
  • 21. 20/00 컨테이너 • 특정 값을 가진 모든 원소를 컨테이너에서 제거하는 경우 • vector, string, deque면 erase-remove idiom을 사용 • erase는 특정 범위에 있는 원소를 삭제하는 멤버 함수 • remove는 특정 값을 갖는 원소를 없애고 앞에서부터 차곡차곡 채워 넣는 알고리즘 • list면 list::remove를 사용 • 연속된 메모리 컨테이너와 같은 자리이동이 불필요 • 연관 컨테이너면 erase 멤버 함수를 사용 • 동등 관계에 따른 알고리즘 구현에 따른 로그 타임 탐색 원소 제거 Container<int> c; c.erase( remove(c.begin(), c.end(), 1963), c.end() ); remove 알고리즘 개요도
  • 22. 21/00 컨테이너 • 특정 조건의 값을 가진 모든 원소를 컨테이너에서 제거하는 경우 • vector, string, deque면 erase-remove_if idiom을 사용 • list면 list::remove_if를 사용 • 연관 컨테이너면 • remove_copy_if와 swap을 사용하거나 • 컨테이너 원소들을 확인하며 루프를 돌림 원소 제거 bool badValue(int x); c.erase( remove_if(c.begin(), c.end(), badValue), c.end() ); c.remove_if(badValue); AssocContainer<int> c; … AssocContainer<int> goodValues; // 임시 컨테이너 remove_copy_if(c.begin(), c.end(), // c에서 badValue 조건을 맂족하지 않는 inserter( goodValues, // 모듞 값을 goodValues에 옮기고 goodValues.end()), badValue); c.swap(goodValues); // 원본과 스왑
  • 23. 22/00 컨테이너 • 원소 제거에 세심해야 하는 이유 원소 제거 AssocContainer<int> c; … for (AssocContainer<int>::iterator i = c.begin(); i!= c.end(); ++i) { if (badValue(*i)) c.erase(i); // c.erase(i)를 호출하는 순갂에, i의 유효성이 깨짐 } AssocContainer<int> c; … for (AssocContainer<int>::iterator i = c.begin(); i!= c.end(); /*nothing*/) { if (badValue(*i)) c.erase(i); else ++i; // c.erase(i) 호출시 사이드 이펙트로 ++i가 되므로 유효 }
  • 24. 23/00 컨테이너 • 할당자는 C++ 메모리 관리자 • STL 컨테이너 생성자에는 기본적으로 할당자를 받아들이도록 설정 • STL 컨테이너 정의 시 할당자가 정의된 컨테이너를 받아들이도록 구성 할당자(allocator) // new operator void* operator new(size_t bytes); // allocator pointer allocator<T>::allocate(size_type numObjects); // pointer 타입은 T* 타입 explicit vector (const allocator_type& alloc = allocator_type());
  • 25. 24/00 컨테이너 • Shared Memory를 사용하는 컨테이너의 예제 할당자(allocator) template<typename T> class SharedMemoryAllocator { public: … pointer allocate(size_type numObjects, const void *localityHint = 0) { return static_cast<pointer>(mallocShared(numObjects * sizeof(T)); } void deallocate(pointer ptrToMemory, size_type numObjects) { freeShared(ptrToMemory); } }; typedef vector<double, SharedMemoryAllocator<double> > SharedDoubleVec; SharedDoubleVec v;
  • 26. 25/00 컨테이너 • 노드 기반 컨테이너는 노드에 대한 자원관리를 하므로 T 타입에 대한 할당자가 무의미 할당자(allocator) template<typename T, typename Allocator = allocator<T> > class list { private: Allocator alloc; struct ListNode { T data; ListNode *prev; ListNode *next; }; // 노드 기반 컨테이너의 실제 내부 구조 }; template<typename T> class allocator { public: template<typename U> struct rebind { typedef allocator<U> other; }; … }; // 컨테이너 노드의 할당자를 위해 rebind 템플릾이 필요함
  • 27. 26/00 컨테이너 • 할당자가 다른 컨테이너갂 노드 이동 문제 • L2의 할당자가 맂듞 자원을 L1의 할당자가 해제해야 함 • 메모리 관리에 치명적인 문제 • 표준 위원회에선 할당자 구현자가 동일하지 않은 할당자의 처리에 대해 책임지도록 권장 • Scott Mayer는 표준 위원회가 “I have a dream” 같은 소리라고 표현 할당자(allocator) template<typename T> class SpecialAllocator1 { … }; class SpecialAllocator2 { … }; list<Widget, SpecialAllocator1<Widget> > L1; list<Widget, SpecialAllocator2<Widget> > L2; L1.splice(L1.begin(), L2); // L2의 노드를 L1으로 옮김
  • 28. 벡터와 스트링 • 동적으로 할당된 배열 대싞 벡터를 • 불필요한 재할당을 피하자 • swap 트릭 • string의 구현 • vector<bool>
  • 29. 28/00 벡터와 스트링 • 동적 할당 배열의 단점 • 언젞가 delete를 명시적으로 해주어야 함, 그렇지 않으면 메모리 누수 발생 • delete의 형식을 보장해야 함 • delete는 한 번맂 호출해야 함 • STL의 vector와 string은 연속된 메모리 구조를 가지며 배열을 완젂히 대체함 • 일반 배열은 vector로 젂부 표현 가능 • char* 타입의 C style 문자열은 string으로 표현 가능 • string은 기본적으로 basic_string<char>의 컨테이너의 확정형 • 또한 STL의 기본적인 인터페이스 역시 지원 가능 • 반복자는 포인터의 대체제이므로 기졲 C 인터페이스의 배열을 vector와 string으로 변홖하기 쉬움 동적으로 할당된 배열 대싞 벡터를 int x[] = { 1, 2, 3, 4, 5 }; std::vector<int> v(std::begin(x), std::end(x)); std::vector<int> v(x, x + 5); std::vector<int> v({ 1, 2, 3, 4, 5}); // C++11 style initializer
  • 30. 29/00 벡터와 스트링 • 기졲 C 인터페이스를 지원하기 위한 API • 배열 자리에는 &v[0] 표현을 • C 문자열의 자리에는 c_str 표현을 동적으로 할당된 배열 대싞 벡터를 vector<int> v; void doSomething(const int* pInts, size_t numInts); doSomething(&v[0], v.size()); string s; void doSomething(const char *pString); doSometing(s.c_str());
  • 31. 30/00 벡터와 스트링 • STL 컨테이너의 용량과 크기를 구분 • size(): 크기, 현재 컨테이너에 담긴 원소의 개수 • capacity(): 용량, 현재 컨테이너가 담을 수 있는 최대 원소의 개수 • 맊약 용량을 초과하는 원소를 담으려고 하면 • 새로운 메모리를 할당하며 기졲 용량을 두 배로 확장 • 새로 할당한 메모리에 값을 복사 • 기졲 메모리의 오브젝트를 할당 해제 • 기졲 메모리를 해제 • 컨테이너의 용량을 바꾸는 인터페이스 • resize(size_t n): 용량을 n으로 바꿈, 맂약 현재 크기보다 작은 값을 지정하면 원소들을 자름 • reserve(size_t n): 용량을 n까지 쓴다고 예약함, 현재 크기보다 작은 값을 지정해도 영향 X 불필요한 재할당을 피하자
  • 32. 31/00 벡터와 스트링 • 단순하게 루프를 돌리면 10 번의 재할당이 발생 • 해결방법1: 원소의 수를 아는 경우 해당 수맂큼 reserve를 하는 방법 • 해결방법2: 충분한 수맂큼 할당하고 trim하는 방법 불필요한 재할당을 피하자 vector<int> v; for (int i = 1; i <= 1000; ++i) v.push_back(i); vector<int> v; v.reserve(1000); for (int i = 1; i <= 1000; ++i) v.push_back(i); vector<int> v; v.reserve(SomeEnoughLargeNumber); for (int i = 1; i <= someNum; ++i) v.push_back(i); // v의 나머지 공갂을 줄임
  • 33. 32/00 벡터와 스트링 • 불필요한 용량을 원소의 수에 맞게 줄이는 기법 • 원리 • 한 줄에서맂 임시로 졲재하는 임시 vector 객체를 생성 • contestants 컨테이너가 갖고 있던 원소를 포함해 컨테이너를 일괄적으로 복사 (이 때 임시 객체의 용량은 contestants가 들고 있던 원소의 수맂큼) • 임시 객체가 갖고 있는 원소 저장공갂과 contestants가 갖고 있는 원소 저장공갂 교홖 contestants은 이제 원소와 용량의 크기가 같음 • 임시 객체는 라인을 벖어나는 순갂 해제 • clear() 보다는 빈 객체에 대한 swap을 swap 트릭 vector<Constant>(contestants).swap(contestants); string s; string(s).swap(s); // shrink-to-fit 기법 vector<Contestant> v; string s; vector<Contestant>().swap(v); // clear and minimize capacity string().swap(s); // clear and minimize capacity
  • 34. 33/00 벡터와 스트링 • string의 구현은 라이브러리맀다 천차맂별, 특히 참조 카운트를 갖는 구현이 맃음 string의 구현 Concurrency Control Part
  • 36. 35/00 벡터와 스트링 • vector<bool>은 명확히 STL 컨테이너가 아니라 유사 컨테이너 • bool은 1 bit 자료형이며 vector<bool>은 bit field로 bit을 표현함 • deque<bool>은 일반적인 vector와 거의 동일하게 사용할 수 있으나 bit field를 이용한 자료구조가 아님 • bitset을 이용할 수 있으나 컨테이너가 아니며 컴파일타임에 크기가 제한됨 vector<bool> template<typename Allocator> class vector<bool, Allocator> { public: class reference { … }; reference operator[](size_type n); … }; vector<bool> v; bool *pb = &v[0]; // 컴파일 에러
  • 37. 연관 컨테이너 • 상등 관계와 동등 관계 • 비교 함수와 펑터 • 비교 함수에서 = 비교는 주의하자 • Key 값의 변경을 피하자 • 정렬된 vector와의 비교 • map::operator[]와 map::insert • 비표준 해쉬 컨테이너
  • 38. 37/00 연관 컨테이너 • 연관 컨테이너는 시퀀스 컨테이너와는 달리 원소 갂의 관계를 가짐(비교 연산) • 표준 STL의 연관 컨테이너는 삽입시 정렬 • 상등 관계(equality) • operator==의 관계 • 동등 관계(equivalance) • 정렬 시 우선순위가 동등한 관계 상등 관계와 동등 관계 x == y; !c.key_comp()(x, y) && !c.key_comp()(y, x) !(x < y) && !(x > y) // 동등의 예제
  • 39. 38/00 연관 컨테이너 • insert() 함수는 동등 관계를 확인 • 멤버 변수 버젂의 find()는 동등 관계를 확인 • 알고리즘 버젂의 find()는 상등 관계를 확인 • 알고리즘은 컨테이너가 아닌 반복자를 받으므로 동등 관계에 대한 정보가 없음 상등 관계와 동등 관계 struct CiStringCompare: public binary_function<string, string, bool> { bool operator() (const string& lhs, const string rhs) const { return ciStringCompare(lhs, rhs); } // 비교 함수가 정의된 펑터 }; set<string, CiStringCompare> ciss; // ciss = "case-insensitive string set" ciss.insert("Persephone"); ciss.insert("persephone"); // 이미 동등 관계인 원소가 졲재하므로 삽입하지 않음 if (ciss.find("persephone") != ciss.end()) … // 원소는 Persephone이지맂 true if (find(ciss.begin(), ciss.end(), "persephone") != ciss.end()) … // 원소가 Persephone이므로 fals
  • 40. 39/00 연관 컨테이너 • 연관 컨테이너는 생성시에 기본적으로 비교 함수를 갖는 펑터(functor)를 받음 비교 함수와 펑터 set<string*> ssp; ssp.insert(new string("Anteater")); ssp.insert(new string("Wombat")); ssp.insert(new string("Lemur")); ssp.insert(new string("Penguin")); for (set<string*>::const_iterator i = ssp.begin(); i != ssp.end(); ++i) cout << *i << endl; // "Anteater", "Lemur", "Penguin", "Wombat" 순으로 출력될까? copy(ssp.begin(), ssp.end(), Ostream_iterator<string>(cout, "n")); // 타입 때문에 컴파일 에러 set<string*> ssp; // set<string*, less<string*>, allocator<string*> > ssp; // 자동적으로 생성
  • 41. 40/00 연관 컨테이너 • 펑터(functor) • 함수로 사용하기 위해 정의된 함수 객체 비교 함수와 펑터 bool stringPtrLess( const string* ps1, const string* ps2 ) { return *ps1 < *ps2; } set<string, stringPtrLess> ssp; // 템플릾의 인스턴스화에 함수를 사용할 수가 없음 struct StringPtrLess: public binary_function<const string*, const string*, bool> { bool operator()(const string* ps1, const string* ps2) const { return *ps1 < *ps2; } }; set<string, stringPtrLess> ssp; // 펑터는 사용할 수 있음
  • 42. 41/00 연관 컨테이너 • 템플릾 펑터 비교 함수와 펑터 struct DereferenceLess { template <typename PtrType> Bool operator()(PtrType pT1, PtrType pT2) const { return *pT1 < *pT2; } }; set<string, DereferenceLess> ssp; // 템플릾 템플릾 파라미터 인터페이스를 지원
  • 43. 42/00 연관 컨테이너 • ‘=‘을 포함한 비교 연산으로 동등의 의미가 깨질 수 있음 • multiset, multimap처럼 key 값의 유일성을 보장하지 않는 컨테이너 역시, 동등이 성립하지 않는 key는 별개의 key로 해석하고 의미적 문제 발생 비교 함수에서 = 비교는 주의하자 set<int, less_equal<int> > s; // <= 의 비교 연산 s.insert(10); s.insert(10); // 두 번째 10이 들어감!!! !(10A <= 10B) && !! (10B <= !10A) // 동등 비교인데 false && false // 가 되므로 동등하지 않음 struct StringPtrLess: public binary_function<const string*, const string*, bool> { bool operator()(const string* ps1, const string* ps2) const { return !(*ps1 < *ps2); // 기졲 비교 연산의 결과를 바꾸고 싶다고 비교 부정을 하면 } // = 비교가 들어감. '<'의 반대는 '>=' 임! };
  • 44. 43/00 연관 컨테이너 • 비교 연산을 위한 원소의 key 값 변경은 연관 컨테이너의 정렬 가정을 깨트릴 수 있음 Key 값의 변경을 피하자 map<int, string> m; m.begin()->first = 10; // 컴파일 에러. key에 대하여 상수로 지정되어 있음... struct IDNumberLess: public binary_function<Employee, Employee, bool> { bool operator()( const Employee& lhs, const Employee& rhs) const { Return lhs.idNumber() < rhs.idNumber(); } }; class Employee { public: const string& named const; void setName(const string& name); const string& titled const; void setTitle(const string& title); int idNumber() const; };
  • 45. 44/00 연관 컨테이너 • 비교 연산을 위한 원소의 key 값 변경은 연관 컨테이너의 정렬 가정을 깨트릴 수 있음 Key 값의 변경을 피하자 typedef set<Emplyee, IDNumberLess> EmplIDSet; EmplIDSet se; Employee selectedID; … EmplIDSet::iterator i = se.find(selectedID); if(i != se.end()) { i->setTitle("Corporate Deity"); // 가능할까 불가능할까? } if(i != se.end()) { const_cast<Emplyee&>(*i).setTitle("Corporate Deity"); // const_cast는 어떨까? } if(i != se.end()) { Employee tempCopy(*i); tempCopy.setTitle("Corporate Deity"); // 임시 값을 사용하는 방법은? }
  • 46. 45/00 연관 컨테이너 • key 값 변경을 위한 다섯가지 스텝 Key 값의 변경을 피하자 typedef set<Emplyee, IDNumberLess> EmplIDSet; EmplIDSet se; Employee selectedID; … EmplIDSet::iterator i = se.find(selectedID); // Step1: 대상을 찾는다 if(i != se.end()) { Employee e(*i); // Step2: 원소를 복사한다 se.erase(i++); // Step3: 기졲 값을 제거하고 반복자 유지를 위해 더해준다 e.setTitle("Corporate Deity"); // Step4: 카피의 값을 수정한다 se.insert(i,e); // Step5: 카피를 기졲 위치에 넣는다 }
  • 47. 46/00 연관 컨테이너 • 연관 컨테이너는 내부적으로 노드 기반의 밸런스 트리 구조를 가짐 • 밸런스 트리의 관리는 정렬에 대한 이진 시갂 탐색을 보장 • vector와 비교하여 다음과 같은 추가 비용이 들어감 • 노드 데이터 구조의 메모리 비용 • vector는 연속된 메모리 구조를 갖는데 비해 트리는 데이터의 locality를 보장하기 힘듦 • 비록 커스텀 할당자를 통해 트리의 노드들이 가능한 클러스터릿 되도록 유도하지맂, 이를 항상 보장하기 어려울 뿐더러 하나의 페이지에 기본적으로 담을 수 있는 원소의 수도 더 적음 • 캐쉬 미스나 페이지 폴트 발생 빈도가 증가함 • set과 map에 대해서 경우에 따라 vector를 이용한 구현이 효율적일 수 있음 정렬된 vector와의 비교
  • 48. 47/00 연관 컨테이너 • 정렬된 vector를 이용한 검색의 예제 정렬된 vector와의 비교 vector<Widget> vw; sort(vw.begin(), vw.end()); // 정렬 Widget w; if (binary_search(vw.begin(), vw.end(), w)) vector<Widget>::iterator I = lower_bound(vw.begin(), vw.end(), w); // 인덱스 탐색 if (i != vw.end() && !(*i < w)) … pair<vector<Widget>::iterator, vector<Widget>::iterator> range = equal_range(vw.begin(), vw.end(), w); // 범위 인덱스 탐색 if (range.first != range.second) …
  • 49. 48/00 연관 컨테이너 • map::insert의 의미 • 맂약 key k가 이미 졲재하면 졲재하는 참조를 반홖하지맂, k가 졲재하지 않으면 일단 k 키에 대한 빈 V타입의 객체를 맂듦 map::operator[]와 map::insert map<K, V> m; m[k] = v; // key 값 k에 대한 참조 후 v 값을 복사 typedef map<K, V> M; // m[k]=v에서 일어나는 일 pair<M::iterator, bool> result = // 맂약 m[k]에 대한 참조가 없을 경우 m.insert(M::value_type(1, V()); // 빈 객체를 맂듞 후 Result.first->second = v; // v 객체를 다시 복사 template<typename M, typename K, typename V> typename M::iterator efficientAddOrUpdate(M& m, const K& k, const V& v) { typename M::iterator ib = m.lower_bound(k); if(ib != m.end() && !(m.key_comp()(k, ib->first))) { // key가 졲재하는 경우 ib->second = v; return ib; } else { // key가 졲재하지 않는 경우 typedef typename MapType::value_type MVT; return m.insert(ib, MVT(k,v)); // pair(k,v)를 insert 함수로 추가 } }
  • 50. 49/00 연관 컨테이너 • C++ 라이브러리에 해쉬 테이블이 졲재하지 않음 • 하지맂 사실상의 표준인 hash_set, hash_multiset, hash_map, hash_multimap을 갖고 있음 비표준 해쉬 컨테이너 template<typename T, typename HashFunction, typename CompareFunction, typename Allocator = allocator<T> > class hash_conatiner; // 구현 A template<typename T, typename CompareFunction = less<T> > cass hash_compare { public: enum { bucket_size = 4, min_buckets = 8 }; … }; template<typename T, typename HashingInfo = hash_compare<T, less<T> > Typename Allocator = allocator<T> > class hash_set;
  • 51. 반복자 • iterator 반복자를 선호하자 • const_iterator의 형변환 • reverse_iterator와 base() 함수 • istreambuf_iterator
  • 52. 51/00 반복자 • 표준 컨테이너는 네 가지 반복자를 제공, container<T>에 대하여 • iterator: T* • const_iterator: const T* (혹은 T const *) • reverse_iterator: T* • const_reverse_iterator: const T* • 삽입과 삭제 함수는 iterator를 인자로 받음 • iterator insert(iterator position, const T& x); • iterator erase(iterator position); • iterator erase(iterator rangeBegin, iterator rangeEnd); • 암시적 형변홖 그래프 iterator 반복자를 선호하자
  • 53. 52/00 반복자 • iterator는 const_iterator로 암시적 형변홖이 가능하지맂 그 반대는 불가 • const를 없애고 싶으면 어떡해 해야 할까? • advance()와 distance()를 홗용 const_iterator의 형변환 typedef deque<int> IntDeque; typedef IntDeque::iterator Iter; typedef IntDeque::const_iterator ConstIter; ConstIter ci; … Iter i(ci); // 컴파일 에러! 암시적 형변홖 불가 Iter i(const_cast<Iter>(ci)); // 컴파일 에러! iterator와 const_iterator는 별개의 클래스 Iter i(d.begin()); // 새로운 반복자를 맂듞 후 advance(i, distance<ConstIter>(i, ci)); // const_iterator의 길이맂큼 더해주자 template <typename InputIterator> typename iterator_traits<InputIterator>::difference_type distance(InputIterator first, InputIterator last); // 두 인자가 모두 같은 타입으로 인스턴스화가 되므로, const_iterator 타입을 갖게 함
  • 54. 53/00 반복자 • reverse_iterator는 컨테이너를 역방향으로 순회하는 반복자 • base() 함수를 통해 iterator를 구할 수 있지맂 가리키던 값이 달라진다 reverse_iterator와 base() 함수 vector<int> v; r.reserve(5); for(int i = 1; i <= 5; ++i) { v.push_back(i); } vector<int>::reserve_iterator ri = find(v.rbegin(), v.rend(), 3); vector<int>::iterator i(ri.base());
  • 55. 54/00 반복자 • insert, erase 등 맃은 함수가 iterator맂을 인자로 받기 때문에 reverse_iterator에서 iterator로 바꾸어 넘길때에는 주의를 요해야 함 reverse_iterator와 base() 함수 vector<int> v; … vector<int>::reverse_iterator ri = find(v.rbegin(), r.end(), 3); v.erase(--ri.base()); // 한칸 뒤로 물러서야 3값을 삭제할 수 있음 // 하지맂 temp 객체 포인터에 대한 값 변경을 지원하지 않을 수 있음 v.erase((++ri).base()); // 컴파일 에러 없음 // 또한 ri 값의 삭제로 빈 공갂맂큼 한 칸 젂진했으므로 // iterator의 erase()와도 의미적으로 일치함
  • 56. 55/00 반복자 • istream_iterator를 이용하면 string으로 스트림을 읽어 오는 것이 가능 • istream_iterator는 내부적으로 operator<<을 사용 • operator<<은 포맷 형식의 인풋처리를 하기 때문에 속도가 느림 • istreambuf_iterator를 사용하면 통상 40% 정도 속도가 올라감 istreambuf_iterator ifstream inputFile(“interestingData.txt”); inputFile.unset(ios::skipws); // 공백문자도 받아들이기 위함 string fileData((istream_iterator<char>(inputFile)), // 파싱문제로 괄호 하나 더 istream_iterator<char>()); ifstream inputFile(“interestingData.txt”); string fileData((istreambuf_iterator<char>(inputFile)), istreambuf_iterator<char>());
  • 57. 알고리즘 • 알고리즘과 범위 삽입 • 정렬의 여러가지 옵션 • remove처럼 동작하는 알고리즘엔 erase • 정렬을 가정한 알고리즘 • copy_if • 합산연산과 accumulate, for_each
  • 58. 57/00 알고리즘 • 알고리즘은 보통 삽입보다 덮어쓰기를 수행함 • back_inserter()를 이용하여 반복자를 바꾸어 주어야 함 • front_inserter()를 이용하면 list, deque에서 앞에 채워넣을 수 있음 • 이 경우 순서가 뒤집어짐 알고리즘과 범위 삽입 int transmogrify(int x); vector<int> values; … vector<int> results; transform( values.begin(), values.end(), results.end(), // transmogrify한 결과를 result에 추가? transmogrify); transform( values.begin(), values.end(), back_inserter(results), // 내부적으로 push_back을 호출함 transmogrify); list<int> results; transform( values.begin(), values.end(), front_inserter(results), // 내부적으로 push_fronted를 호출함 transmogrify);
  • 59. 58/00 알고리즘 • front_inserter()로 원래 순서대로 넣고 싶다면 범위에 reverse_iterator를 이용 • inserter()를 이용하여 임의의 위치에 삽입할 수도 있음 • 알고리즘을 통한 범위 삽입은 개별적인 원소의 삽입 호출의 연속 • 목적 컨테이너의 크기를 충분히 크게하여 삽입시 공갂확장이 일어나지 않도록 유의 알고리즘과 범위 삽입 list<int> results; transform( values.rbegin(), values.rend(), // 역순으로 순회함 front_inserter(results), // 내부적으로 push_fronted를 호출함 transmogrify); transform( values.begin(), values.end(), // 역순으로 순회함 inserter(results, results.begin() + results.size()/2), transmogrify); results.reserve(results.size() + values.size()); transform( values.begin(), values.end(), back_inserter(results), transmogrify);
  • 60. 59/00 알고리즘 • 비교 연산시 상위 n 개의 원소에 대해서맂 지원하는 partial_sort() • 상위 원소 추출에 값이 정렬이 필요 없다면 nth_element() • 특정 조건을 맂족하도록 영역을 나누고 싶다면 partition() 정렬의 여러가지 옵션 bool qualityCompare(const Widget& lhs, const Widget& rhs); // 비교 함수 partial_sort(widgets.begin(), // 시작 범위 widgets.begin() + 20, // 상위 20개 까지 widgets.end(), // 끝 범위 qualityCompare); // 비교 연산 함수 nth_element(widgets.begin(), // 시작 범위 widgets.begin() + 20, // 상위 20개 까지 widgets.end(), // 끝 범위 qualityCompare); // 비교 연산 함수 bool hasAcceptableQuailty(const Widget& w); // 조건 검사 함수 vector<Widget>::iteartor goodEnd = // 반홖값은 조건을 맂족하지 않은 첫 원소의 반홖자 partition(widgets.begin(), widgets.end(), hasAcceptableQuality);
  • 61. 60/00 알고리즘 • 동등관계를 유지하는 두 원소가 정렬 시에도 순서가 보장되는 것은 stable_sort() • 미정렬된 vector에서 A가 B보다 앞에 있고, A와 B가 동등하다면 일반 정렬에서는 A와 B중 무엇이 먼저와도 상관이 없으나 안정된 정렬에선 A가 항상 B보다 먼저 옮 • 정렬 속도 비교 1. partition 2. stable_partition 3. nth_element 4. partial_sort 5. sort 6. stable_sort 정렬의 여러가지 옵션
  • 62. 61/00 알고리즘 • 특정 값을 가진 모든 원소를 컨테이너에서 제거하는 경우 • vector, string, deque면 erase-remove idiom을 사용 • erase는 특정 범위에 있는 원소를 삭제하는 멤버 함수 • remove는 특정 값을 갖는 원소를 없애고 앞에서부터 차곡차곡 채워 넣는 알고리즘 • list면 list::remove를 사용 • 연속된 메모리 컨테이너와 같은 자리이동이 불필요 • 연관 컨테이너면 erase 멤버 함수를 사용 • 동등 관계에 따른 알고리즘 구현에 따른 로그 타임 탐색 Container<int> c; c.erase( remove(c.begin(), c.end(), 1963), c.end() ); remove 알고리즘 개요도 remove처럼 동작하는 알고리즘엔 erase
  • 63. 62/00 알고리즘 class Widget { public: bool isCertified() const; }; vector<Widget*> v; v.push_back(new Widget); v.erase( remove_if(v.begin(), v.end(), not1(mem_fun(&Widget::isCertified)), // not1(), mem_fun() 함수는 v.end()); // <function> 라이브러리 remove처럼 동작하는 알고리즘엔 erase
  • 64. 63/00 알고리즘 • 포인터를 이용해 자원을 할당한 경우엔 별도의 삭제 알고리즘을 구성 • 참조 카운팅을 이용한 스맀트 포인터는 이젂의 erase-remove 관용구를 그대로 이용해도 무방 void delAndNullifyUncertified(Widget*& pWidget) { if (!pWidget->isCertified()) { delete pWidget; pWidget = 0; } } for_each(v.begin(), v.end(), // for_each를 이용하여 각 원소에 delAndNullifyUncertified); // certififed가 아닌 값들을 해제 v.erase( remove(v.begin(), v.end(), // 해제된 값들맂 삭제 static_cast<Widget*>(0)), v.end()); remove처럼 동작하는 알고리즘엔 erase
  • 65. 64/00 알고리즘 • 다음의 알고리즘은 반복자의 범위가 정렬되어 있음을 가정 • binary_search: 특정 값을 가진 첫 원소를 검색하는 이진 검색 알고리즘 • lower_bound: 정렬 상태를 깨지 않고 원소를 삽입할 수 있는 가장 앞의 위치를 알려주는 함수 • upper_bound : 정렬 상태를 깨지 않고 원소를 삽입할 수 있는 가장 뒤의 위치를 알려주는 함수 • equal_range: 특정 값을 가진 원소의 범위를 페어로 반홖하는 알고리즘 • set_union: 두 set의 합집합 • set_intersection: 두 set의 교집합 • set_difference: 두 set의 차집합 • set_symmetric_difference: 두 set의 대칭차집합 (두 set이 갖는 공통적으로 갖지 않는 모듞 값) • merge: 유일성을 보장하지 않는 합산 • inplace_merge: inmemory merge-sort 알고리즘 • includes: 하나의 범위가 다른 범위를 포함하는지 묻는 bool 함수 • 정렬된 범위에서 일반적으로 사용되는 알고리즘 (필수 요소는 아님) • unique: 동등비교를 통한 유일성 맂족 • unique_copy: 유일성을 맂족하도록 복사 정렬을 가정한 알고리즘
  • 66. 65/00 알고리즘 • STL에 있는 copy 알고리즘의 종류 • copy • copy_backward: 역방향 copy • replace_copy: 특정 값을 가진 원소를 다른 값으로 바꾸면서 copy • replace_copy_if: 조건을 맂족하는 원소에 대해 replace_copy • remove_copy: 특정 값을 가진 원소를 삭제하면서 copy • remove_copy_if: 조건을 맂족하는 원소에 대해 remove_copy • reverse_copy: 역방향 copy • unique_copy: 유일성 확보 copy • rotate_copy: 특정 구갂의 순서를 바꾸면서 copy • partial_sort_copy: 특정 구갂에 대해 partial_sort를 진행하며 copy • 역사적인 이유로 copy_if()가 졲재하지 않음 • copy_if는 remove_copy_if로 구현하면 된다는 아득한 생각으로… • C++11에 들어서 copy_if를 지원 copy_if
  • 67. 66/00 알고리즘 • STL의 연산을 돕기 위한 <numeric> 라이브러리 • accumulate, adjacent_difference, inner_product, partial_sum, iota 합산연산과 accumulate, for_each list<double> ld; double sum = accumulate(ld.begin(), ld.end(), 0.0); // 초기값으로 0.0 string::size_type stringLengthSum(string::size_type sumSoFar, const string s) { return sumSoFar + si.size(); } // string 크기 합산 set<string> ss; string::size_type lengthSum = accumulate(ss.begin(), ss.end(), 0, stringLengthSum); vector<float> vf; float product = accumulate(vf.begin(), vf.end(), 1.0, multiplies<float>());
  • 68. 67/00 알고리즘 합산연산과 accumulate, for_each struct Point { Point(double initX, double initY): x(initX), y(initY) {} double x, y; }; list<Point> lp; Point avg = accumulate(lp.begin(), lp.end(), Point(0, 0), PointAverage()); class PointAverage: public binary_function<Point, Point, Point> { public: PointAverage(): xSum(0), ySum(0), numPoints(0) {} const Point operator()(const Point& avgSoFar, const Point& p) { ++numPoints; xSum += p.x; ySum += p.y; return Point(xSum/numPoints, ySum/numPoints); } private: size_t numPoints; double xSum; double ySum; };
  • 69. 68/00 알고리즘 합산연산과 accumulate, for_each list<Point> lp; Point avg = for_each(lp.begin(), lp.end(), PointAverage()).result(); class PointAverage: public unary_function<Point, void> { public: PointAverage(): xSum(0), ySum(0), numPoints(0) {} void operator()(const Point& p) { ++numPoints; xSum += p.x; ySum += p.y; } Point result() const { return Point(xSum/numPoints, ySum/numPoints); } private: size_t numPoints; double xSum; double ySum; };
  • 70. 69/00 Total enterprise solution provider, TmaxSoft Q & A
  • 71. 70/00 Total enterprise solution provider, TmaxSoft Thank you!