51
Effect STL by Hermet Effective STL 1 / 53 [email protected]

Effective STL

  • Upload
    heba

  • View
    181

  • Download
    0

Embed Size (px)

DESCRIPTION

Effective STL. [email protected]. 효과적인 컨테이너 요리법 vector 와 string STL 연관 컨테이너 반복자 ( iterators ) 알고리즘 (algorithms) 함수자 , 함수 객체 , 함수 , 기타 등등 STL 프로그래밍을 더 재미있게 해주는 팁 모음 * 본 자료는 오직 개인적인 학습을 목표로 두었으며 어떠한 상업적 목표도 없음을 밝힙니다 . - PowerPoint PPT Presentation

Citation preview

Page 1: Effective STL

Effect STL by Her-met

Effective STL

1 / 53

[email protected]

Page 2: Effective STL

Effect STL by Her-met

1. 효과적인 컨테이너 요리법2. vector 와 string3. STL 연관 컨테이너4. 반복자 (iterators)5. 알고리즘 (algorithms)6. 함수자 , 함수 객체 , 함수 , 기타 등등7. STL 프로그래밍을 더 재미있게 해주는 팁 모음

* 본 자료는 오직 개인적인 학습을 목표로 두었으며 어떠한 상업적 목표도 없음을 밝힙니다 . * 본 자료는 Scott Meyer 저자의 Effective STL ( 정보 문화사 ) 의 서적을 토대로 제작되었음을 밝힙니다 .

Contents

2 / 53

Page 3: Effective STL

Effect STL by Her-met

1. 효과적인 컨테이너 요리법시작해 볼까요 ?

3 / 53

Page 4: Effective STL

Effect STL by Her-met

a. 거의 모든 컨테이너들은 같은 이름의 헤더에 정의▫ vector - <vector>▫ list - <list>▫ set, multiset - <set>▫ map, multimap - <map>

b. 네 개를 제외한 모든 알고리즘이 <algorithm> 에 정의▫ 제외된 네 개 accumulate, inner_product, adjacent_difference, partial_sum ▫ 위의 네 개는 <numeric> 에 선언되어 있음 .

c. istream_iterator 와 istreambuf_iterator 를 포함한 특별한 종류의 반복자는 <iterator> 에 정의

d. 표준 함수자와 함수자 어댑터는 <functional> 에 선언▫ 표준 함수자 예 – less<T>▫ 함수자 어댑터 예 – not1, bind2nd

1. 용도에 맞는 헤더를 항상 #include 하자 ( 항목 48)

4 / 53

Page 5: Effective STL

Effect STL by Her-met

1. 표준 시퀀스 컨테이너 : vector, string, deque, lista. Vector 는 기본적으로 사용되는 시퀀스b. List 는 시퀀스 중간에 빈번한 삽입 , 삭제가 수행될 필요가 있을 시 사용c. Deque 는 대부분의 삽입과 삭제가 시퀀스 앞과 끝에에서 일어날 때 사용

2. 표준 연관 컨테이너 : set, multiset, map, multimap

3. 비표준 시퀀스 컨테이너 : slist, ropea. slist : 단일 연결 리스트b. rope: 대용량 string

4. string 대신 사용되는 vector<char>

5. 표준 연관 컨테이너 대신 사용되는 vector

6. STL 에 속하지 않는 표준 컨테이너a. bitset, valarray, stack, queue, priority_queue

2. 적재적소에 알맞은 컨테이너를 사용하자 - 1( 항목 1)

5 / 53

Page 6: Effective STL

Effect STL by Her-met

1. 연속 메모리 컨테이너 와 노드 기반 컨테이너a. 연속 메모리 컨테이너 : 새 요소가 삽입되거나 이미 있던 요소가 지워지면 같은 메모리 단위에 있던 다른 요소들은 앞 혹은 뒤로 밀려나면서 새 요소가 삽입될 공간을 만들든지 지워진 공간을 메움 . ‘ 밀어내기’ 때문에 수행 성능의 발목을 잡을 수 있다 . - vector, string, deque, rope

b. 노드 기반 컨테이너 : 삽입 혹은 삭제되었을 때 노드의 포인터만 영향을 받지 , 나머지 요소들이 밀려나는 일이 없다 . – list, slist

2. 적재적소에 알맞은 컨테이너를 사용하자 - 2( 항목 1)

6 / 53

Page 7: Effective STL

Effect STL by Her-met

1. 컨테이너의 아무 위치에 요소를 삽입할 수 있어야 하나 ?-시퀀스 컨테이너를 사용하세요~2. 요소들의 순서 결정에 직접 관여할 필요가 없다면 ?-해쉬 컨테이너를 사용하세요~3. 임의 접근 반복자가 필요하다면 ?-vector, deque, string, rope 등을 고려해 보세요 ~4. 요소 삽입이나 삭제시 다른 요소들이 밀려나는 일이 없어야 한다면 ?-당연하겠지만 연속 메모리 컨테이너에 손 대지 말 것 !5. 데이터가 c의 데이터 타입과 메모리 배열 구조적으로 호환되어야 한다면 ?-vector 밖에 없습니다 .6. 탐색 속도가 가장 중요하다면 ?-해쉬 컨테이너 , 정렬된 vector, 표준 연관 컨테이너 중 하나 .7. 컨테이너 참조 카운팅이 신경 쓰인다면 ?-string을 피하라~8. 삽입과 삭제를 안정적으로 되돌릴 수 있어야 한다면 ?-노드 기반 컨테이너를 고려해 보세요 ~9. 반복자 , 포인터 , 참조자가 무효화되는 일을 최소화 해야 한다면 ?-노드 기반 컨테이너를 사용하세요 ~10. 시퀀스 컨테이너가 필요한데 , 요소 삭제가 일어나지 않고 요소 삽입이 컨테이너 끝에서만 일어난다면 ? 포인터와 참조자가 무효화되지 않아야 한다면 ?-정답은 deque입니다 .

2. 적재적소에 알맞은 컨테이너를 사용하자 - 3( 항목 1)

7 / 53

Page 8: Effective STL

Effect STL by Her-met

1. 컨테이너 안에 객체에는 복사되어 들어가고 복사되어 나온다는 사실 . 따라서 복사 생성자와 복사 대입 연산자가 사용되므로 항상 염두해 두세요 .

class Widget {

public:Widget( const Widget& ) { … }Widget& operator=(const Widget& ) { … }

};

2. 포인터 컨테이너를 사용할 경우 포인터를 복사하되 스마트 포인터가 괜찮은 방법 중 하나입니다 .

3. 복사는 컨테이너 안의 객체에 맞게 비용은 최소화하며 , 동작은 정확하게 하자 ( 항목 3)

8 / 53

Page 9: Effective STL

Effect STL by Her-met

• class SP{

public:     M* m_M;    M* operator->(){ return m_M;}};

• void main(){    SP A;    A->m_I=10; // (A.m_M)->m_I=10; 과 같은 코드 ;         A->Fm(); // (A.m_M)->Fm(); 과 같은 코드 ;}

• 출력결과는 물론 10이겠죠 ...주석처리한 코딩으로 바꾸어도 같은 결과를 얻습니다 .그리고 ,A->m_I라는 표현은 (A.operator->())->m_M 과도 같은 표현 입니다 .

잠시 ! 스마트 포인터 (Smart Pointer) 란 ?

9 / 53

Page 10: Effective STL

Effect STL by Her-met

1. If( c.size() == 0 ) -> if( c.empty() == true )a. empty 는 인라인 함수로 구현되어 있음 .b. empty 는 모든 표준 컨테이너에 대해 상수 시간에 수행되지만 , 몇몇 제품에서 구현된 list 클래 스에선 size 가 선형 시간에 수행되는 경우가 많다 . c. 따라서 , empty 가 더 빠르다 .

4.size() 결과를 0 과 비교할 생각이라면 차라리 empty()를 호출하자 ( 항목 4)

10 / 53

Page 11: Effective STL

Effect STL by Her-met

1. 벡터 v1 과 v2 가 있을 때 , v1 의 내용을 v2 의 뒤쪽 반과 똑같이 만드는 가장 빠른 방법은 ?

a.int index = 0;

for( itr = v2.begin() + v2.size() / 2 ; itr < v2.end(); ++itr ) { v1[ index ] = itr->second;++index;

}

5. 단일 요소를 단위로 동작하는 멤버 함수보다 요소의 범위 를 단위로 동작하는 멤버 함수가 더 낫다 -1( 항목 5)

b. v1.assign( v2.begin() + v2.size() / 2, v2.end() );컨테이너 내용을 다른 범위의 것과 대체하고 싶다면 대입 (assign) 하라 !

c. 장점 ? - 코드가 대게 짧다 . - 훨씬 명확하고 간결한 의미가 전달된다 .

11 / 53

Page 12: Effective STL

Effect STL by Her-met

알고리즘을 사용한 범위 단위의 멤버 함수의 예 .1.

int data[ numValues ];vector< int > v;

v.insert( v.begin(), data, data + numValues ); //insert 사용시 요소를 마지막 위치까지 바로 옮기도록 되어 있음 . 따라서 for 문에 비해 n*( numValues + 1) 번의 이동 횟수가 줄어든 것과 같음 .

2. vector<Widget> v1, v2; ………. v1.clear(); copy( v2.begin() + v2.size() / 2, v2.end(), back_inserter( v1 ) );

// 컨테이너의 메모리가 꽉 찼을 경우 for 문으로 요소를 삽입할 시 , 하나 삽입할 때마다 저장할 메모리 를 할당 하는 반면 , 범위 버전의 함수를 사용할 경우 처음 , 한번에 필요한 메모리를 할당해 버리기 때 문에 성능상 우월

5. 단일 요소를 단위로 동작하는 멤버 함수보다 요소의 범위 를 단위로 동작하는 멤버 함수가 더 낫다 -2( 항목 5)

12 / 53

Page 13: Effective STL

Effect STL by Her-met

참고사항1. 범위 생성 : 모든 표준 컨테이너는 다음과 같은 형태의 생성자를 지원container :: container( InputIterator begin, InputIterator end );

2. 범위 삽입 : 모든 표준 컨테이너는 다음과 같은 형태의 insert 를 지원void container :: insert( iterator position, InputIterator begin, InputIterator end );void container :: insert( InputIterator begin, InputIterator end );

3. 범위 삭제 : 역시 표준 컨테이너는 범위 버전의 erase 를 제공iterator container :: erase( iterator begin, iterator end ); <- 시퀀스 컨테이너void container :: erase( iterator begin, iterator end ); <- 연관 컨테이너

4. 범위 대입 : 모든 표준 시퀀스 컨테이너는 범위 버전의 assign 을 제공void container :: assign( InputIterator begin, InputIterator end );

5. 단일 요소를 단위로 동작하는 멤버 함수보다 요소의 범위 를 단위로 동작하는 멤버 함수가 더 낫다 -3( 항목 5)

13 / 53

Page 14: Effective STL

Effect STL by Her-met

vector< int > v;v.reserve( 10 );for( int I = 0; I < 10; ++I ) v.push_back( I );

v[3] = v[5] = v[9] = 99;remove( v.begin() , v.end(), 99 );cout << v.size(); -> ?

6. 요소를 정말 제거 하고 싶다면 remove 류의 알고리즘 뒤 에 꼭 erase 를 붙여서 사용하자 ! ( 항목 32)

답은 10.“remove 는 어느 것도 진짜로 없애지 않는다 ! 없앨 수 없기 때문이다 .”

해결책 v.erase( remove( v.begin(), v.end(), 99 ), v.end() ); cout << v.size(); -> 7

1 2 3 99 5 7 8 999 99

1 2 3 ?5 7 8 9 ? ?

14 / 53

Page 15: Effective STL

Effect STL by Her-met

vector< Widget*> v;

Widget* w = new Widget();v.push_back( w );….delete v[0];v.erase( v.begin() + 0 );

…더 멋지게 삭제할 수 있을까 ?

7.New 로 생성한 포인터 컨테이너를 사용할 때는 컨테이너가 소멸되기 전에 포인터를 delete 하는 일은 잊지 말자 ! ( 항목 7)

template< typename T > struct DeleteObject : public unary_function< const T*, void > { void operator()( const T* ptr ) const {

delete ptr; } };

void doSomething() {for_each( vwp.begin(), vwp.end(), DeleteObject<Widget>() );

}

…더 멋지게 구현할 순 없을까 ?

struct DeleteObject { template< typename T >

void operator()( const T* ptr ) const {delete ptr;

} };

void doSomething() {deque< SpecialString* > dssp;for_each( dssp.begin(), dssp.end(), DeleteObject() );

}

15 / 53

Page 16: Effective STL

Effect STL by Her-met

8. 데이터를 삭제할 때에도 조심스럽게 선택할 것이 많다 . ( 항목 9)

1. 연속 메모리 컨테이너 ( vector, deque, string ) : erase-remove 합성문 .-> c.erase( remove( c.begin(), c.end(), 1963 ), c.end() );

2. 양방향 반복자를 지원하는 list 및 list 는 멤버함수인 remove-> c.remove( 1963 );

3. 연관 컨테이너에서 특정한 값을 가진 요소를 지우는 것은 erase-> c.erase( 1963 );

4. 술어구문 , true 를 반환화는 요소를 지우고 싶다면 ? remove_if-> bool badValue( int x );-> c.erase( remove_if( c.begin(), c.end(), badValue ), c.end() ); //vector, string, deque -> c.remove_if( badValue ); // list

참고 ) vector, string, deque 에 속해 있는 연속 메모리 컨테이너는 erase 를 호출하면 지워진 요소를 가리키는 반복자만 무효화되는 것이 아니라 그 외의 모든 반복자가 무효화가 된다 !

16 / 53

Page 17: Effective STL

Effect STL by Her-met

9.STL 컨테이너의 쓰레드 안정성에 대한 기대는 현실에 맞추 어 가지자 . -1 ( 항목 12)

1. 여러 쓰레드에서 읽는 것은 안전하다 . – 하나 이상의 쓰레드가 하나의 컨테이너의 내용을 동시에 읽어 내는 경우가 있는데 , 제대로 동작합니다 . 그러나 읽기 도중에 쓰기 동작이 수행되면 안되겠죠 .

2. 여러 쓰레드에서 다른 컨테이너에 쓰는 것은 안전하다 . – 하나 이상의 쓰레드가 다른 컨테이너에 동시에 쓸 수 있다 .

3. 그럼 쓰레드의 안정성을 보장하려면 ?a. 컨테이너의 멤버 함수를 호출하는 시간 동안에 컨테이너에 락을 걸기b. 컨테이너가 만들어 내어 주는 반복자의 유효 기간 동안에 컨테이너에 락을 걸기c. 컨테이너에 대해 실행된 알고리즘의 수행 시간 동안에 컨테이너에 락을 걸기

vector< int > v;….vector< int > :: iterator first5( find( v.begin(), v.end(), 5 ) );If( first5 != v.end() ) { v.erase( first5 ); }

한 쓰레드에서 요소 를 찾는 중 다른 쓰레드에서 요소를 삽입한다면 ? 반복자는 무효화가 되어 문제 발생 !

17 / 53

Page 18: Effective STL

Effect STL by Her-met

9.STL 컨테이너의 쓰레드 안정성에 대한 기대는 현실에 맞추 어 가지자 . -2 ( 항목 12)

방법 Avector< int > v;….getMutexFor( v );vector< int > :: iterator first5( find( v.begin(), v.end(), 5 ) );If( first5 != v.end() ) { v.erase( first5 ); }releaseMutexFor( v );

방법 Btemplate< typename Container > class Lock {public:Lock( const Container& container ) : c( container ) { getMutexFor( c ) );~Lock() { releaseMutexFor( c ) };private:const Container& c;};

vector< int > v;{ Lock< vector<int> > lock( v );vector< int > :: iterator first5( find( v.begin(), v.end(), 5 ) );If( first5 != v.end() ) { v.erase( first5 ); } } //블록이 끝나면 Lock 소멸자 호출

18 / 53

Page 19: Effective STL

Effect STL by Her-met

2. vector 와 string

할만 한데 ?

19 / 53

Page 20: Effective STL

Effect STL by Her-met

1. 동적 할당된 배열 보다는 vector 와 string 이 낫다 . ( 항목 13)

가장 큰 이유 ?string 은 참조 카운팅이 동작되도록 구현되어 있는데 , 불필요한 메모리 할당과 문자 복사를 없앰으로써 높은 수행성능 보장만약 메모리가 문제 되지 않고 참조 카운팅이 부담스럽다면 , 참조 카운팅이 없는 vector< char >를 고려해 보아라 .

20 / 53

Page 21: Effective STL

Effect STL by Her-met

2.reserve 는 필요 없이 메모리가 재할당되는 것을 막아준다 . ( 항목 14)

1. 컨테이너에 담을 수 있는 최대 용량은 max_size 를 호출해 보면 알 수 있다 .

2. 재할당의 경우 다음과 같은 네 단계로 진행된다 .a. 컨테이너의 현재 용량의 몇 배가 되는 메모리 블록을 새로 할당한다 . 보통 2 배 만큼 늘림 .b. 컨테이너가 원래 가지고 있었던 메모리에 저장된 모든 요소 데이터를 새 메모리에 복사한다 .c. 원래의 메모리에 저장된 모든 객체를 소멸 시킨다 .d. 원래의 메모리를 해제 합니다 .

춘언 왈 ) 헉 ! 이거 완젼 쇼킹이구만 0_0;;

3. 따라서 !사용할 컨테이너 메모리를 미리 reserve 로 할당해 둠으로써 자동 재할당의 횟수를 최소화 시켜 주자 !비용 부담을 상당히 줄일 수 있다 .

size() - 현재 컨테이너에 들어 있는 요소 개수 알려줌capacity() – 현재 컨테이너의 할당된 메모리로 담을 수 있는 요소의 개수를 알려 줌resize( size_t n ) - 컨테이너가 담을 수 있는 요소의 개수를 n 으로 만듬reserver( size_t n ) – 컨테이너의 용량을 최소 n 개로 맞춤

21 / 53

Page 22: Effective STL

Effect STL by Her-met

3.vector<bool> 보기를 돌같이 하자 ( 항목 18)

vector<bool> v;bool* pb = &v[0];

컴파일이 안되네요 -0-;

그 이유는 공간을 줄이기 위해 bool 을 압축시킨 데이터 표현 방식을 쓰기 때문에 컴파일에서 실패합니다 . 대게 “ vector” 에 저장되는 “ bool” 을 하나의 비트로 나타내어 한 바이트에 여덟 개의 bool 을 담을 수 있게 구현합니다 .

그럼 어떡하죠 ?1. deque<bool> 2. bitset

22 / 53

Page 23: Effective STL

Effect STL by Her-met

3. STL 연관 컨테이너…………………….

23 / 53

Page 24: Effective STL

Effect STL by Her-met

1. 상등 관계와 동등 관계의 차이를 파악하자 . ( 항목 19)

find 알고리즘 같은 경우 상등성 (equality) 이 기준인 반면 , set :: insert 는 동등성 ( equivalence )가 기준 .

무슨 차이지 ?상등성 : operator == 에 뿌리를 두고 있음 .

class Widget {private:

TimeStamp lastAccessed;};

bool operator==( const Widget& lhs, const Widget& rhs ) {//lastAccessed 는 고려하지 않는다고 정의

}

그럼 두 개의 Widget 은 lastAccessed 필드 값이 다르더라도 두 객체는 같다는 판정 .

동등성 : 정렬 순서를 생각하면 됩니다 .x < c, y < cx 와 y 는 동등하다고 판정 .

24 / 53

Page 25: Effective STL

Effect STL by Her-met

2. 연관 컨테이너용 비교 함수는 같은 값에 대해 false 를 반환 해야 한다 .( 항목 21)

set< int, less_equal<int> > s; //s 는 <= 에 의해 정렬됩니다 .s.insert( 10 );s.insert( 10 );

먼저 들어간 , 10 과 나중에 들어간 10 을 비교하게 된다 .less_equal 를 이용해 비교함수 <= 가 쓰인다 .

!( 10A <= 10B) && !( 10B <= 10A );!( true ) && !( true )false && falseFalse

따라서 둘은 동등하지 않다고 결론이 난다 .

Set 에는 같은 값이 들어갈 수 없다는 사실에 위반된다 .

따라서 ,struct StringPtrGreater : public binary_function< const string*, const string*, bool > {

bool operator()( const string* ps1, const string* ps2 ) const { return *ps1 > *ps2; // return *ps1 >= *ps2; ( x )

}};

25 / 53

Page 26: Effective STL

Effect STL by Her-met

3. set 과 multiset 에 저장된 데이터 요소에 대해 키 (key) 를 바꾸는 일은 피하자 ( 항목 22)

map< int, string > m;m.begin()->first = 10; //error!

Multimap< int, string > mm;mm.begin()->first = 20; //error also!

에러의 원인은 pair< const K, V > 즉 , 키 (KEY) 부분이 const K 이기 때문이다 .

그런데 , set 과 multiset 은 키가 const 가 아니므로 변경이 가능하다 .

하지만 , 키 부분을 건들려면 컨테이너는 엉망이 되고 이 컨테이너는 예기치 못한 동작을 수행하며 ,모든 책임을 여러분의 것이 된다는 사실 !

- 내부 로직을 알 필요까진 없어요 ! 우리는 이러한 사실만 알고 그냥 주의하기만 합시다 .-

26 / 53

Page 27: Effective STL

Effect STL by Her-met

4. map ::operator[] 와 map ::insert 는 효율 문제에 주의하여 선택하자 ( 항목 24)

map< int, Widget > m;m[1] = 1.50; m[2] = 3.67;

Map 의 [] 연산자는 “추가 아니면 갱신” 기능을 수행하도록 설계되어 있습니다 .

map< K, V > m; m[ k ] = v;라는 표현 식이 있다면 , 우선 해당 맵에 k 가 있는 지 점검하고 없다면 , k 와 v 가 한쌍으로 묶여서 맵에 새로 추가된다 . 만약 있다면 , k 와 맵핑된 값이 v 로 갱신됩니다 .

m[1] = 1.50; 에서 m[1] 은 사실 m.operator[](1) 과 같은 표현 . 이 함수는 Wdiget 에 대한 참조자를 반환하도록 되어 있음 . 이런 절차로 m[1] = 1.50 은 operator[] 가 기본 생성자를 사용해서 Widget 객체를 만들고 1 과 함께 묶은 페어 객체를 만든 후 , Widget 객체에 대한 참조자를 반환함 . 그리고 참조자를 통해 1.50 값을 대입 .

위의 식을 다음과 같이 고쳐 쓸 수 있으며typedef map< int, Widget > IntWidgetMap;m.insert( IntWidgetMap ::value_type( 1, 1.50 ) );이는 Widget 객체를 임시로 만드는 데 필요한 기본 생성자 함수 , 소멸자 함수 , 대입 연산자 함수가 호출되지 않으므로 비용 절감 효과는 큼 .

결론 ? 맵에 추가할 때는 insert. 갱신할 때는 operator[]

27 / 53

Page 28: Effective STL

Effect STL by Her-met

5. 현재는 표준이 아니지만 , 해쉬 컨테이너에 대해 충분히 대 비해 두자 ( 항목 25)

1. 해쉬 컨테이너 중에 가장 대표적인 것은 SGI 버전과 딩컴웨어 (Dinkumware) 버전 .

4. SGI 버전은 단일 연결 리스트로 저장하고 5. 딩컴웨어 버전은 이중 연결 리스트로 저장한다는 사실 .

그런데 해쉬 컨테이너의 특징이 무엇일까 ?

28 / 53

Page 29: Effective STL

Effect STL by Her-met

4. 반복자 (Iterators)

역시 지루해 지기 시작했다 .

29 / 53

Page 30: Effective STL

Effect STL by Her-met

1. const_iterator 나 reverse_iterator, const_reverse_iterator 도 좋지만 역시 쓸만한 것은 itera-tor 이다 . - 1( 항목 26)

vector<T> insert 와 erase 시그너처 iterator insert( iterator position, const T& x );iterator erase( iterator position );iterator erase( iterator rangeBegin, iterator rangeEnd );

알아둘 점은 , const_iterator 도 안되고 reverse_iterator 와 const_reverse_iterator 는 더더욱 안된다는 사실 . 무조건 iterator!

iterator

const_iterator

reverse_iterator

const_reverse_iterator

base()

base()

변환 가능한 관계도

30 / 53

Page 31: Effective STL

Effect STL by Her-met

1. const_iterator 나 reverse_iterator, const_reverse_iterator 도 좋지만 역시 쓸만한 것은 itera-tor 이다 . - 2( 항목 26)

iterator 가 좋은 이유1. 어떤 형태의 insert 와 erase 멤버 함수는 무조건 iterator 만 넘겨야 한다 .2. const_iterator 를 iterator 로 암묵적으로 변환할 방법은 없으며 결국 일반성이 떨어저 효율에 대한 보장도 할 수 없음 .3. reverse_iterator 를 iterator 로 변환할 수는 있으나 , 변환한 후에는 약간의 조정이 필요 .

31 / 53

Page 32: Effective STL

Effect STL by Her-met

typedef deque< int > IntDeque; //typedef 참 편리하죠 ?typedef IntDeque :: iterator Iter;typedef IntDeque :: const_iterator ConstIter;

ConstIter ci; //const_iterator 라는 사실 명심하시고 .…Iter i( ci ); //Error! 암묵적 캐스팅 불가 !Iter I( const_cast< iter > ( ci ) ); //역시 Error! 캐스팅 또한 불가 !

그럼 해결책은 ?

2.const_iterator 를 iterator 로 바꾸는 데에는 dis-tance 와 advance 를 사용하자 ( 항목 27)

IntDeque d;Iter i( d.begin() ); //iterator i 를 begin() 으로 초기화advance( i, distance< ConstIter >( i, ci ) ); //ci 가 있는 곳을 i 로 옮긴다 . 아이구 간단하네 .

* 참고로 const_iterator 를 iterator 로 변환하는데 걸리는 시간은 선형 시간 !

32 / 53

Page 33: Effective STL

Effect STL by Her-met

reverse_iterator 에서 base 멤버함수를 호출하면 iterator 를 얻을 수 있다고 했었죠 ?하지만 , 정확하게 그러하지는 않는다는 사실 -_-

vector< int > v; v.reserve( 5 );

for( int i = 1; i <= 5; ++i ) { v.push_back( i ); }

vector< int > ::reverse_iterator ri = find( v.rbegin(), v.rend(), 3 ); vector< int >:: iterator i( ri.base() );

그런데…

3.reverse_iterator 에 대응되는 기점 반복자 (base it-erator) 를 사용하는 방법을 정확하게 이해하자 - 1 ( 항목 28)

1 2 3 4 5

rend()

begin()

rbegin()

end()

ri

i

해답은 ? vector< int > :: iterator i( (--ri).base() );

33 / 53

Page 34: Effective STL

Effect STL by Her-met

도대체 이따구로 만든 이유가 뭐지 ?

3.reverse_iterator 에 대응되는 기점 반복자 (base it-erator) 를 사용하는 방법을 정확하게 이해하자 - 1 ( 항목 28)

1 2 3 4 5

rend()

begin()

rbegin()

end()

ri

i

흐흐 . 아무 이유 없습니다 . 그들이 또라이라서 그럽니다 .

보통 삽입 연산이 발생하면 , 반복자가 가리키고 있는 요소의 앞에 추가하게 된다는 사실 .

34 / 53

Page 35: Effective STL

Effect STL by Her-met

텍스트 파일을 string 객체에 복사한다면 ?ifstream inputFile(“광호뱃살 .txt”);string fileData( ( istream_iterator< char >( inputFile ) ), istream_iterator< char >() );

//inputFile 을 읽어 fileData 에 저장한다 .그럼 위의 문제는 ?

4. 문자 단위의 입력에는 istreambuf_iterator 의 사용도 적 절하다 ( 항목 29)

Istream_iterator 는 스트림 읽기 수행 시 operator >> 함수를 사용하며 , 이 함수는 공백 문자를 건너 뛰어 버린다 .

그럼 어떻게 해야 하나요 ?

ifstream inpuFile(“천재춘언 .txt”);inputFile.unsetf( ios :: skipws ); // 공백 문자를 건너 뛰지 못하도록 설정String fileData( ( istream_iterator< char >( inputFile ) ), istream_iterator< char >() );

하지만 , 이보다 성능상 더 강력한 존재가 있으니 바로 istreambuf_iterator

ifstream inputFile(“승찬카리스마쌍꺼풀 .txt”);string fileData( ( istreambuf_iterator< char >( inputFile ) ), istreambuf_iterator< char >() );skipws 플래그 설정 따윈 안해줘도 된답니다 .

35 / 53

Page 36: Effective STL

Effect STL by Her-met

5. 알고리즘 (Algorithms)

아 , 끝나냐 마냐 ?

36 / 53

Page 37: Effective STL

Effect STL by Her-met

int trasnmogrify( int x );vector< int > values;…vector<int> results;transform( values.begin(), values.end(), results.end(), transmogrify );

results.end() 에는 아무것도 없기 때문에 이는 버그를 유발합니다 .

1. back_inserter 를 사용하면 되겠죠 .

vector< int > results;transform( values.begin(), values.end(), back_inserter( results ), transmogrify );

단 , back_inserter 가 반환하는 반복자는 push_back 을 꼭 지원해야 합니다 .

2. reserve 로 메모리를 미리 확보해 둡니다 . results.reserve( results.size() + values.size() );transform( values.begin(), values.end(), results.end(), transmogrify );

1. 알고리즘의 데이터 기록 범위는 충분히 크게 잡자 ( 항목 30)

37 / 53

Page 38: Effective STL

Effect STL by Her-met

1. vector, string, deque, 혹은 c++ 배열에 대한 전체 정렬을 수행할 필요가 있을 때에는 sort 나 stable_sort를 사용하자 .

2. vector, string, deque, 혹은 c++ 배열에 대해 상위 n 개의 요소만 순서에 맞추어 뽑아내고 싶다면partial_sort 를 사용하자 .

3. vector, string, deque, 혹은 c++ 배열에 대해 상위 n 개의 요소만 뽑되 순소는 고려할 필요가 없다면 , nth_element 가 적합하다 .

4. 표준 시퀀스 컨테이너가 있고 , 이 컨테이너의 요소들을 어떤 기준에 만족하는 것들과 그렇지 않은 것들을 모아 구분하고 싶다면 partition 혹은 stable_partition 을 찾자 .

5. 사용하고 있는 데이터가 list 인 경우에 partition 과 stable_partition 은 직접 사용할 수 있으며 sort 와 sta-ble_sort 알고리즘 대신에 list :: sort 멤버 함수를 사용할 수 있다 .

6. 표준 연관 컨테이너를 사용할 경우에는 모든 요소가 자동적으로 정렬된 상태에 있게 된다 .

7. 아래는 ( 시간과 메모리 ) 를 적게 먹는 순서대로 정렬해본 것 .a. partition b. stable_partition c. nth_element d. partial_sort e. sort f. stable_sort

2. 정렬시의 선택 사항들을 제대로 파악해 놓자 - 1( 항목 31)

38 / 53

Page 39: Effective STL

Effect STL by Her-met

partial_sort 예제bool qualityCompare( const Widge& lhs, const Widget& rhs ) {

// 정렬 기준에 대한 소스를 짭니다 .}

…partial_sort( widgets.begin(), widgets.begin() + 20,

widgets.end(), qualityCompare );

2. 정렬시의 선택 사항들을 제대로 파악해 놓자 - 2( 항목 31)

39 / 53

Page 40: Effective STL

Effect STL by Her-met

1. 탐색 알고리즘 ( 로그 시간 )binary_search : 특정 원소의 존재 여부 판단lower_bound : 값으로 들어온 값보다 작거나 같은 값을 가지는 원소의 위치 반환upper_bound : 값으로 들어온 값보다 크거나 같은 값을 가지는 원소의 위치 반환equal_range : 정렬 상태가 깨지지 않는 첫 번째 , 마지막 위치 검색2. 집합 조작 알고리즘 ( 선형 시간 )set_union : 두 정렬된 범위의 합집합을 계산한다 .set_intersection : 두 정렬된 범위의 교집합을 계산한다 .set_difference : 두 정렬된 범위의 차집합을 계산한다 .set_symmetric_difference : 두 정렬된 범위의 대칭 차집합을 계산한다 .

3. 병합 정렬 알고리즘 ( 선형 시간 )merge : 두 개의 정렬된 범위를 받아 합치고 다시 새로운 정렬된 범위를 생성inplace_merge : 연속적으로 정렬된 범위를 병합includes : 어떤 범위 안에 우리가 원하는 값이 있는지의 여부를 알아볼 때

3. 정렬된 범위에 대해 동작하는 알고리즘이 어떤 것들이 있는지 파악해 두자 ( 항목 34)

40 / 53

Page 41: Effective STL

Effect STL by Her-met

copy – 첫 번째 원소로 시작하는 범위를 복사한다 .copy_backward – 마지막 원소로 시작하는 범위를 복사한다 .replace_copy – 원본의 범위를 복사한 후 replace 를 실행한다 .reverse_copy – 문서를 뒤바꾸는 동안 원소를 복사한다 .replace_copy_if – 원본의 범위를 복사한 후 replace_if() 를 실행unique_copy – 원본의 범위를 복사한 후 unique() 실행remove_copy – 원본의 범위를 복사한 후 remove() 실행rotate_copy – 순서를 회전하는 동안 원소를 복사remove_copy_if – 원본의 범위를 복사한 후 remove_if 실행partial_sort_copy – copy() 와 partial_sort() 가 결합된 상태uninitialized_copy – 조사해 볼 것 .

그런데 .. copy_if 는 표준 알고리즘에 없다는 사실 . 적절한 라이브러리를 구해서 사용하시길 바랍니다 . ( HP 사의 STL 추천 )

4. copy_if 를 적절히 구현해 사용하자 ( 항목 36)

41 / 53

Page 42: Effective STL

Effect STL by Her-met

1. Accumulate 는 두가지 형태가 있습니다 .

a. 범위 내의 값의 합을 초기 값에 더한 결과를 반환 .list< double > ld;double sum = accumulate( ld.begin(), ld.end(), 0.0 ); // 초기값은 반드시 float !

b. 초기값과 요약용 함수를 받아 동작하는 더 일반적인 버전 .string ::size_type stringLengthSum( string ::size_type sumSoFar, const string& s ) {

return sumSoFar + s.size();} // 참고 : size_type 은 어떤 것을 셀 때 사용하는 타입set< string > ss;string :: size_type lengthSum = accumulate( ss.begin(), ss.end(), 0, stringLengthSum );

* 추가로 범위 내의 수를 곱하는 multiplies 라는 표준 함수자 클래스도 있다 . float product = accumulate( vf.begin(), vf.end(), 1.0, multiplies< float >() );

5. 범위 내의 데이터 값을 요약하거나 더할 때에는 accumulate 나 for_each 를 사용하자 . - 1( 항목 37)

42 / 53

Page 43: Effective STL

Effect STL by Her-met

2. for_each 는 accumulate 와 다른 점이라면 ,a. accumulate 는 “범위를 요약한다”는 느낌이 강한 반면 , for_each 는 “범위 내의 모든 요소에 대해 어떤 일을 한 다” 는 느낌을 준다 .b. accumulate 는 원하는 요약 결과를 바로 반환하지만 , for_each 는 요약 정보를 뽑아내야 함 .

class PointAverage : public unary_function< Point, void > {private:size_t numPoints;double xSum;double ySum;

public:PointAverage() : xSum( 0 ), ySum( 0 ), numPoints( 0 ) { … }void operator() ( const Point& p ) {++numPoints;ySum += p.x;ySum += p.y;}

Point result() const { return Point( xSum / numPoints, ySum / numPoints ); }};

List<Point> lp;….Point avg = for_each( lp.begin(), lp.end(), PoinAverage() ).result();

5. 범위 내의 데이터 값을 요약하거나 더할 때에는 accumulate 나 for_each 를 사용하자 . - 2( 항목 37)

43 / 53

Page 44: Effective STL

Effect STL by Her-met

6. 함수자 , 함수 객체 , 함수 , 기타 등등

아따 , 빨랑 끝내제는 !

44 / 53

Page 45: Effective STL

Effect STL by Her-met

술어 구문 : BOOL값을 반환하는 함수순수 함수 : 이 함수가 반환하는 값이 그 함수의 매개 변수에 종속된 함수 .

ex ) f 가 순수 함수일 때 x, y 가 객체이면 , f( x, y ) 의 반환값은 x 나 y 의 값이 바뀔 때만 변할 수 있다 .

술어 구문 클래스 : OPERATOR() 가 술어 구문인 함수자 클래스 .

1. 함구 객체는 값으로 전달되게 해라 . ( 항목 38)

2. 술어 구문은 순수 함수로 만들자 . ( 항목 39)

3. less<T> 는 operator< 의 의미임을 알고 있자 . ( 항목 42 )

45 / 53

Page 46: Effective STL

Effect STL by Her-met

7. STL 프로그래밍을 더 재미있게 해주는 팁 모음

zzz…

46 / 53

Page 47: Effective STL

Effect STL by Her-met

1. 어설프게 손으로 작성한 루프보다는 알고리즘이 더 낫 다 .( 항목 43)

그 이유 ?1. 알고리즘은 직접 만든 루프보다 자주 훨씬 효율적이다 .2. 에러가 일어날 가능성이 더 적다 .3. 훨씬 깨끗하고 간명한 코드로 유지 보수가 쉽다 .4. 알고리즘 제작자는 그 내부를 충분히 파악하고 있으므로 최적화가 되 어 있을 가능성이 높다 .5. 알고리즘 제작자는 컴퓨터 공학적인 알고리즘을 사용한다 .

for( list<Widget> :: iterator i = lw.begin(); i!= lw.end(); ++i ) {

i->redraw();}

for_each( lw.begin(), lw.end(), mem_fun_ref( &Widget ::redraw ) );

47 / 53

Page 48: Effective STL

Effect STL by Her-met

그 이유 ?1. 멤버 함수가 더 빠르다 .2. 멤버 함수는 해당 컨테이너와 더 잘 맞물려 있다는 점 .3. 표준 연관 컨테이너의 멤버함수는 선형 시간이 아닌 로그시간4. 맵이나 멀티맵 같은 경우 키 부분을 다룰 필요가 없다 .5. 알고리즘으로 객체 제거시 , remove, remove_if, unique 등으로 호출해 서는 안되고 erase 를 반드시 붙여주어야 한다는 것 .

2. 같은 이름을 가진 것이 있다면 일반 알고리즘 보다 멤버 함수가 더 낫다 .( 항목 44)

Set< int > s;…Set<int> :: iterator I = s.find( 727 ); // 아하 , 보기에도 더 편하구먼 !Set<int > :: iterator I = find( s.begin(), s.end(), 727 );

48 / 53

Page 49: Effective STL

Effect STL by Her-met

3. count, find, binary_search, lower_bound, upper_bound, 그리고 equal_range 를 제대로 파악해 두자 . ( 항목 45)

질문 ?사용할 알고리즘 사용할 멤버 함수

비 정렬 정렬 set, map multisetmultimap

원하는 값이 있나 ? Find Binary_search

Count find

원하는 값이 있다면 첫 번째 객체는 어디에 있나 ?

Find Equal_range Find Find Lower_bound

원하는 값의 앞에 있지 않는 값을 가진 첫째

객체는 어디 ?

Find_if Lower_bound

Lower_bound Lower_bound

원하는 값의 뒤에 있는 값을 가진 첫째 객체는

어디 ?

Find_if Upper_bound

Upper_bound Upper_bound

원하는 값의 객체 개수 ? Count Equal_range Count count

원하는 값을 가진 객체는 모두 어디 있나 ?

Find(계속 호출 )

Equal_range Equal_range Equal_range

49 / 53

Page 50: Effective STL

Effect STL by Her-met

4. 알고리즘 매개 변수로 함수 대신 함수 객체가 괜찮다 .( 항목 46)

5. 쓰기 전용 코드는 만들지 말자 . ( 항목 47)

6. 에러 메시지 분석 능력을 키우자 . ( 항목 48 )

STL 관련 웹 사이트SGI STL : http://www.sgi.com/tech/stl/STLport : http://www.stlport.org/Boost : http://www.boost.org/

50 / 53

Page 51: Effective STL

Effect STL by Her-met

자 , 그만 잠에서 깨어날 시간입니다 .-END-

51 / 53