티스토리 툴바


파이썬은 모든 것이 클래스이다.
따라서, 여러 자료구조들 간에 형변환이 가능하다. (서프라이즈~!)
STL과 비슷하지만 서로간에 형변환이 된다는 것은.. 가히 놀랄만하다.
STL이나 자료구조를 조금 훑기라도 했다면 들어봤을법한 키워드이므로.. 설명은 생략.

1. 리스트
# 리스트 - [] 연산자로 묶어서 정의
>> colors = ['red', 'green', 'gold']
>> colors
['red', 'green', 'gold']
>> type(colors) # type()은 인자로 준 자료형이 무엇인지 반환하는 함수


# 리스트 - 값 추가
>> colors.append('blue')
>> colors
['red', 'green', 'gold', 'blue']
>> colors.insert(1, 'black')
>> colors
['red', 'black', 'green', 'gold', 'blue']
>> colors.extend(['white', 'gray'])
>> colors
['red', 'black', 'green', 'gold', 'blue', 'white', 'gray']
>> colors += ['red']
>> colors
['red', 'black', 'green', 'gold', 'blue', 'white', 'gray', 'red']
>> colors += 'red'
['red', 'black', 'green', 'gold', 'blue', 'white', 'gray', 'red', 'r', 'e', 'd']

# 리스트 - index() 
>> colors.index('red')
0
>> colors.index('red', 1) # 시작점 명시 (순차 탐색인듯)
7

# 리스트 - 그외
>> colors.count('red')
2
>> colors.pop() # 뒤에서 값을 뽑아내고 삭제
'red'
>> colors.pop(1) # index로 pop()
'black'
>> colors.remove('gold') # 해당 값 삭제, 2개 있다면 앞 쪽에 있는 값 삭제

# 리스트 - 정렬
>> colors.sort() # 순방향 정렬
>> colors.reverse() # 역방향 정렬



2. 세트
# 세트(집합) - {} 연산자로 묶어서 정의
>> a = {1, 2, 3}
>> b = {3, 4, 5}
>> a
{1, 2, 3}
>> b
{3, 4, 5}
>> type(a)


# 세트 - 집합 연산
>> a.union(b) # 합집합('|' 연산자로도 가능 ex: a | b)
{1,2,3,4,5}
>> a.intersection(b) # 교집합('&' 연산자로도 가능 ex: a & b)
{3}
>> a - b # 차집합
{1, 2}


Posted by 전산쟁이폴

1. 변수명
제약사항은 C와 동일 함.
# 파이썬 예약어
and, as, assert, break, class, continue, def, del, elif, else, except, is, 
finally, for, from, global, if, import, in, is, lambda, nonlocal, not, or, pass, raise, return, try, while, with, yield 


2. 수치
수치형 - int, long, float, complex
정수 앞에 '0o' 를 붙이면 8진수, '0b'를 붙이면 2진수, '0x'를 붙이면 16진수로 인식

원하는 진수로 변경해주는 함수
oct(), hex(), bin()

수치에 대한 연산
 +, -, *, /, %, **(거듭제곱), //(정수나누기 = 결과값이 정수)


3. 문자
파이썬은 문자를 단일인용부호(')나 다중인용부호(")로 묶어서 표현함.
즉, 쿼터로 시작하면 쿼터, 더블쿼터로 시작하면 더블쿼터로 마무리 해야 함.

문자열에서 제공하는 기본 연산
더하기 (+) - 문자열 상수끼리만 가능 ("py" + "thon")
곱하기 (*) - 문자열 반복 ('py' * 3 = 'pypypy')

문자열 인덱싱

>> a = "python" >> a[0] 'p' >> a[5] 'n' #문자열 꺼내기 (슬라이싱) - 변수 뒤에 [시작위치:끝위치 >> a[0:1] 'p' >> a[1:4] 'yth' >> a[:2] 'py' >> a[-2:] 'on' # 문자열과 수치들간의 변환 >> str(3.14) '3.14' >> int("49") 49 >> float(23) 23.0

Posted by 전산쟁이폴

파이썬이 C보다 먼저 발표 되었더라면 세상이 훨씬 아름다워져 있을 것이다. - 지은이(최종진)

이 책이 끝나갈 무렵, 지은이와 같은 공감대가 형성되었으면 한다.

1. 파이썬의 특징
가독성, 풍부한 라이브러리, 접착성, 무료, 유니코드, 동적타이핑

어떠한 한 언어에 대해 특징을 얘기한다는 것은 그 언어에 전문적인 사람들의 주관적인 견해인듯 하다.
따라서... 별 얘기안함.

가독성 - 코드 블럭을 들여쓰기로 구분
풍부한 라이브러리 - 라이브러리가 많고 확장성이 뛰어남.
접착성 - C로 구현된 모듈을 쉽게 만들어서 붙일 수 있음.
무료 - Python Software Foundation License를 따르며 거의 무료임.
유니코드 - 모든 문자열이 유니코드
동적타이핑 - 런타임 상황에서 타입 확인 및 자신이 만든 함수 호출이 가능

2. 파이썬의 종류
Cpython - C로 작성된 파이썬, 기본적으로 파이썬은 Cpython을 의미함.
Jython - 자바로 작성된 파이썬, 자바 가상 머신(JVM)상에서 작동이 가능함.
IronPython - .Net과 Mono용으로 개발된 파이썬, C#으로 구현되어 있음.
PyPy - 파이썬으로 구현된 파이썬, Cpython보다 빠르게 수행되는것을 목표로 함.

3. Hello world! 출력하기

print("Hello world!")
끄읕...-_ -? 끝!

4. 들여쓰기
파이썬에서는 들여쓰기가 코드블럭이 된다.
따라서, 들여쓰기를 잘못하면 실행자체가 안될 수 있다.
Posted by 전산쟁이폴

필요에 의해서가 아닌, 그러나 심심풀이도 아닌!! 스크립트 언어를 하나 배워볼까 한다.
MFC로 작업을 하다보면 테스트가 필요한 경우가 많은데 간단한 모듈 테스트를 하려고 해도
MFC 프로젝트를 만들기는 부담스럽고.. C, C++ 라이브러리로는 안되는 것들이 많아서 많은(?) 사람들이
추앙하는 파이썬을 배워볼까 한다.

교재 - 빠르게 활용하는 Python3 프로그래밍
실습 도구
http://python.org/download/ - python-3.2.2(x86)
http://www.aptana.com/ - Aptana_Studio_3_Setup_3.0.6

책에서 실습은 python에 내장된 edit로 하는 것 같은데... VS에 익숙해진 나는... 이게 싫다 -_ -;
이것저것 찾아봤는데 파이썬 하나 하자고 eclipse 설치하기도 뭐해서 Aptana studio를 사용함.
Aptana studio 인터페이스는 거의 eclipse와 동일한데 이것저것 설정을 만지다 보니 자꾸 죽는다 -_-;;

아무튼.


window - perferences 설정 변경 


코딩 준비 완료!  (하노이 탑 문제 푸는 중..)
Posted by 전산쟁이폴

출처 : http://blog.naver.com/hermet?Redirect=Log&logNo=103985593

오늘 정리해볼 내용은 C/C++에서의 네임 맹글링 입니다.

 

뭐 실제 프로그래밍에 있어서 거의 도움될 내용은 아니지만,

평소에 궁금하셨던 분 또는 심심하신 분들은 그냥 재미삼아 한번 읽어보세요. ㅋ

 

본 게시물을 읽기 전에 이전 게시물 extern "C" (http://hermet.pe.kr/tb/87864741) 과 콜링 컨벤션 (http://hermet.pe.kr/51152911)을  먼저 참고하시면 도움이 될 것입니다.

 

 

 

그럼 시작하겠습니다. (Here we go~)  ..... (to Hell... 응?)

 

 

정확하게 네임 맹글링 ( Name Mangling ) 또는 네임 데코레이션 ( Name Decoration ) 이라고 하는데,

 

컴파일러는 각 코드 파일을 컴파일하면서 함수 이름들을 변경하게 됩니다. 이는 각 파일마다 존재할 수 있는 동일한 함수명들을 링커(Linker)가 구분하기 위해서이죠.

 

컴파일러는 함수에 대해서 함수의 이름, 파라미터, 콜링 컨벤션(Calling Convention), 네임 스페이스(Namespace) 등을 사용하여 심볼 이름을 만들어 내는데,  사실 이는 컴파일러마다 규칙이 다를 수 있으며 기본적으로 대략 다음 순서로 만들어집니다. (아래 기준은 ms 사의 컴파일러)

 

 

1. prefix '?'

2. 클래스 이름을 제외한 "함수 이름". 

      2.1 만약 함수가 연산자, 생성자, 소멸자일 경우 다음과 같은 2문자로 대처됨

            2.1.1   생성자 - "?0"

            2.1.2   소멸자 - "?1"

            2.1.3   operator new - "?2"

            2.1.4   operator =  - "?3"

            2.1.5   operator + - "?H"

            2.1.6   operator++ - "?E"

           2.1.7  그 외의 것들은 생략... 직접 컴파일해서 확인해 보세요... -_-a

      2.2 그렇지 않으면 함수 이름 끝에 '@'

4. 함수가

     4.1 클래스 멤버함수 였다면,

            4.1.1  해당 멤버 함수의 "클래스 이름Q" ( 만약 inner 클래스에 포함되어 있다면, inner -> outer 순으로. )

            4.1.2  '@'

            4.1.3  'Q'

     4.2 일반 함수면,

            4.2.1 '@'

            4.2.2 'Y'

5. 콜링 컨벤션에 따른 시그니처

      5.1 __cdecl - 'A'

      5.2 __fastcall - 'I'

      5.3 __stdcall - 'G'

      5.4 __thiscall -'E'

      5.5 __fortran - 'C'

      5.6 __pascal - 'C'

6. 함수의 반환 타입에 따른 시그니처  (아래 MS C++ Type codes 참고 )

7. 함수 파라미터에 따른 시그니처 (아래 참고)  (argument 리스트의 마지막에 '@')

8. 'Z'

 

void - 'X'

bool - '_N'

char - 'D'

signed char - 'C'

unsigned char - 'E'

short - 'F'

unsigned short -'G'

int - 'H'

unsigned int - 'I'

long - 'J'

unsigned long - 'K'

__int64 - '_J'

unsigned __int65 - '_K'

wchar_t - 'G'

float - 'M'

double - 'N'

long double - 'O'

struct/class - 'V'

array - 'Q'

pointer - 'P'

funtion pointer - "P6"

<MS C++ Type codes >

 

deault - 'A'

near - 'A'

const - 'B'

volatile - 'C'

const volatile -'D'

far - 'E'

const far - 'E'

volatile far - 'G'

const volatile far - 'H'

huge - 'I'                  // 응? 뭐지 -_-?

<MS C++ Storge class codes >

 

near - 'Y' / 'Q'

far - 'Z' / 'R'

<MS C++ Function calling distance codes >

 

default - '?A'

const - '?B'

volatile - '?C'

const volatile - '?D'

<MS C++ Storage class codes for return >

 

default - 'Q'

static - 'S'

virtual - 'U'

<MS C++ Member function modifier codes >

 

default - 'A'

const - 'B'

volatile - 'C'

const volatile -'C'

<MS C++ Member function modifier codes >

 

 

그럼 실제 예를 통해 한번 확인해 보도록 합시다. 한번 위에서 설명한 네임 맹글링 룰과 매칭해서 비교해 보세요.

 

void Print();                              ->   ?Print@@YAXXZ

Test ::Print();                            ->  ?Print@Test@@QAEXXZ

Test ::Test2 ::Print();                  ->  ?Print@Test2@Test@@QAEXXZ

void Print( int , char* );               ->  ?Print@@YAXHPAD@Z

 

 

 

마지막으로, 심볼 이름으로부터 실제 함수 명을 확인할 수 있는 툴을 설명하자면, 비주얼 스튜디오에 undname 이라는 프로그램이 있습니다.

 

 

심볼 이름을 먼저 확인한 후,

 

 

undname 프로그램으로 확인해 봅니다.

 

 

그리고 리눅스에서도 물론 c++filt 또는 nm 이라는 프로그램이 있지요.

 

지금 리눅스가 작동안하는 관계로... 사용방법만 적어보도록 하겠습니다. (그런 쌩뚱맞을 수가! -_- )

 

nm Print.o | c++filt

nm --demangle Print.o

 

 

 

(참고 - http://www.kegel.com/mangle.html#operator

        - Windows 구조와 원리, 한빛 미디어, 정덕영 저, 115 - 118)

 

Posted by 전산쟁이폴
STL :: 컨테이너(map, multimap)

출처 :
http://blog.daum.net/coolprogramming/77

map과 multimap은 '연관 컨테이너'입니다. 모든 연관 컨테이너(set, multiset, map, multimap)는 '노드 기반 컨테이너'입니다. 또 모든 연관 컨테이너는 균형 2진 트리로 구현됩니다. 균형 2진 트리는 보통 레드-블랙 트리를 사용합니다. map은 key와 value의 쌍으로 구성된 데이터 셋의 집합입니다. 여기서 key는 유니크합니다. multiset도 map과 같이 key와 value의 쌍으로 구성된 데이터 셋의 집합입니다. 단, multiset은 map과 달리 중복된 key가 존재할 수 있습니다.

 

map의 주요 개념을 그림으로 표현하면

map_개념.png 

set과 map은 데이터의 key가 유니크합니다.

 

multiset의 주요 개념을 그림으로 표현하면

multimap_개념.png 

multiset과 multimap은 같은 key값의 데이터를 컨테이너에 저장할 수 있습니다.

 

map, multimap의 주요 특징과 함수

 map도 set처럼 데이터를 추가하는 push_? 류의 함수를 가지고 있지 않습니다. 삽입(insert)한다는 의미에서 insert() 함수를 가지고 있습니다.

map의 특징은 key와 value의 쌍으로 데이터를 저장합니다. 그리고 key값을 이용하여 빠르게 value값을 찾을 수 있습니다. 연관 컨테이너에서 유일하게 [] 연산자 중복을 제공합니다.

 

기본적인 map 예제

#include<iostream>
#include<map>
using namespace std;
voidmain( )
{
    map<int , int > m;

    m[5] = 100;
    m[3] = 50;
    m[7] = 80;
    m[2] = 100;
    m[4] = 100;

    cout << m[5] << endl;
    cout << m[3] << endl;
    cout << m[7] << endl;
    cout << m[2] << endl;
    cout << m[4] << endl;
}
  1. 100
    50
    80
    100
    100

map은 key와 value의 쌍으로 구성된 데이터들의 집합입니다. []연산자의 인덱스는 key이며 인덱스가 가리키는 메모리 값이 value입니다.

주의할 점은 m[5] = 100 연산에서 5라는 key가 없으면 노드 '추가'가되며 5라는 key가 있으면 '갱신'이 됩니다. 아래에서 공부합니다.

그림으로 설명하면..

map_간단_코드.png

map의 트리 그림입니다.

map_데이터_추가.png 

map의 key값 5로 value를 접근하는 그림입니다.

map_데이터_접근.png 

 

 map은 key와 value의 쌍을 pair 객체로 저장합니다. STL의 쌍을 이루는 모든 요소는 pair 객체를 사용합니다.

위 예제는 insert()함수를 사용하여 아래 예제로 바꿀 수 있습니다.

#include<iostream>
#include<map>
using namespace std;
voidmain( )
{
    map<int , int > m;

    m.insert( pair<int,int>( 5, 100) );
    m.insert( pair<int,int>( 3, 50) );
    m.insert( pair<int,int>( 7, 80) );
    m.insert( pair<int,int>( 2, 100) );
    pair<int,int> pr( 4, 100);
    m.insert( pr );

    cout << m[5] << endl;
    cout << m[3] << endl;
    cout << m[7] << endl;
    cout << m[2] << endl;
    cout << m[4] << endl;
}

  1. 100
    50
    80
    100
    100

 map은 key, value를 pair 객체로 각각 first와 second에 저장합니다.

그림으로 표현하면

map_pair_객체.png 

 

 

반복자를 사용한 모든 key, value 출력 예제

#include<iostream>
#include<map>
using namespace std;

voidmain( )
{
    map<int , int > m;

    m[5] = 100;
    m[3] = 50;
    m[7] = 80;
    m[2] = 100;
    m[4] = 100;

    map<int, int>::iterator iter;
    for( iter = m.begin(); iter != m.end() ; iter++)
        cout << "m["<<(*iter).first <<"]: " << (*iter).second << endl;

    cout << "==============" << endl;
    for( iter = m.begin(); iter != m.end() ; iter++)
        cout << "m["<<iter->first <<"]: " << iter->second << endl;
}
  1. m[2]: 100
    m[3]: 50
    m[4]: 100
    m[5]: 100
    m[7]: 80
    ==============
    m[2]: 100
    m[3]: 50
    m[4]: 100
    m[5]: 100
    m[7]: 80

 map도 양방향 반복자를 제공합니다.

그림으로 간단하게..

 map_반복자.png

 

 

연관 컨테이너는 모두 동일한 함수군을 가지며 동작 방식도 같습니다.

map의 검색 함수들입니다.

#include<iostream>
#include<map>
using namespace std;

voidmain( )
{
    map<int , int > m;

    m[5] = 100;
    m[3] = 50;
    m[7] = 80;
    m[2] = 100;
    m[4] = 100;

    map<int, int >::iterator iter;

    iter = m.find( 5 );
    if( iter == m.end() )
        cout << "없음!" << endl;
    else
        cout << "m["<<iter->first <<"]: " << iter->second << endl;

    iter = m.lower_bound( 5 );
    if( iter == m.end() )
        cout << "없음!" << endl;
    else
        cout << "m["<<iter->first <<"]: " << iter->second << endl;

    iter = m.upper_bound( 5 );
    if( iter == m.end() )
        cout << "없음!" << endl;
    else
        cout << "m["<<iter->first <<"]: " << iter->second << endl;

    pair< map<int, int>::iterator, map<int, int>::iterator> iter_pair;
    iter_pair = m.equal_range(5);

    if( (iter_pair.first) == m.end() && (iter_pair.second == m.end()) )
        cout << "없음!" << endl;
    else
    {
        cout << "m["<< iter_pair.first->first <<"]: " << iter_pair.first->second <<" ~ ";
        cout << "m["<< iter_pair.second->first <<"]: " << iter_pair.second->second <<endl;
    }
}
  1. m[5]: 100
    m[5]: 100
    m[7]: 80
    m[5]: 100 ~ m[7]: 80

 동작은 set에서 설명했으므로 패스~!

그림으로 설명하면...

map_검색_함수.png 

 

 

 

마지막으로 multimap 예제입니다.

map과 다른 점은 [] 연산자가 없으며 key 값이 중복될 수 있습니다.

#include<iostream>
#include<map>
using namespace std;

voidmain( )
{
    multimap<int , int > m;

    m.insert( pair<int,int>( 5, 100) );
    m.insert( pair<int,int>( 3, 50) );
    m.insert( pair<int,int>( 7, 80) );
    m.insert( pair<int,int>( 2, 100) );
    m.insert( pair<int,int>( 4, 100) );
    m.insert( pair<int,int>( 7, 200) );
    m.insert( pair<int,int>( 7, 300) );

    pair<multimap<int,int>::iterator, multimap<int, int>::iterator > iter_pair;
    iter_pair = m.equal_range( 7 );
    multimap<int,int>::iterator iter;
    for( iter = iter_pair.first ; iter != iter_pair.second ; iter++)
        cout << "m["<< iter->first <<"]: " << iter->second <<' ';
    cout << endl;

}
  1. m[7]: 80 m[7]: 200 m[7]: 300

 설명은 multiset과 같습니다.

아래는 위 예제의 그림입니다.

multimap_equal_range(1).png  

'Programming > C++/STL' 카테고리의 다른 글

STL :: 컨테이너(set, multiset)  (0) 2011/01/08
STL :: 컨테이너(map, multimap)  (0) 2011/01/08
STL :: 컨테이너(set, multiset)  (0) 2011/01/08
STL :: 컨테이너(deque)  (0) 2011/01/08
STL :: 컨테이너(string)  (0) 2011/01/08
STL :: 컨테이너(list)  (0) 2011/01/08
Posted by 전산쟁이폴
STL :: 컨테이너(set, multiset)

출처 :
http://blog.daum.net/coolprogramming/77

이번 장은 트리 자료구조를 이해하시면 조금 빠르게 공부할 수 있습니다.

 set과 multiset은 '연관 컨테이너'입니다. 모든 연관 컨테이너는 '노드 기반 컨테이너'입니다. 또 모든 연관 컨테이너는 균형 2진 트리로 구현됩니다. 균형 2진 트리는 보통 레드-블랙 트리를 사용합니다. set의 특징은 유니크(unique)한 데이터(key) 셋의 균형 2진 트리로 저장합니다. multiset은 같은 데이터(key) 값을 가질 수 있는 set입니다. 그래서 set과 multiset은 데이터(key) 값을 빠르게 검색하고자 할 때 사용하면 좋은 컨테이너입니다.

 

set의 주요 개념을 그림으로 표현하면

set_개념.png 

set과 map은 컨테이너의 저장 데이터가 유니크합니다.

 

multiset의 주요 개념을 그림으로 표현하면

multiset_개념.png 

multiset과 multimap은 같은 값의 데이터를 컨테이너에 저장할 수 있습니다.

 

1, set과 multiset의 주요 특징과 함수

set은 데이터를 추가하는 push_? 류의 함수를 가지고 있지 않습니다. set뿐만 아니라 모든 연관 컨테이너는 데이터를 트리에 삽입(insert)한다는 의미에서 insert() 함수를 가지고 있습니다. 모든 연관 컨테이너에 데이터를 삽입하면 균형 2진 트리 형태로(비교 함수자를 사용하여) 크기 비교를 하여 2진 트리를 유지합니다.

또 모든 연관 컨테이너는 저장된 데이터를 빠르게 검색할 목적으로 사용되므로 검색류의 함수들을 가지고 있습니다.

 

데이터(key)의 저장과 출력( 랜덤으로 데이터를 저장합니다.)

 #include<iostream>
#include<set>
using namespace std;
voidmain( )
{
    set<int> s;

    s.insert( 50 );
    s.insert( 10 );
    s.insert( 80 );
    s.insert( 30 );
    s.insert( 70 );
    s.insert( 60 );

    set<int>::iterator iter;
    for( iter = s.begin(); iter != s.end() ; iter++)
        cout << *iter << ' ';
    cout << endl;
}
  1. 10 30 50 60 70 80

 set은 같은 값의 데이터(key)를 저장할 수 없는 컨테이너입니다. 모든 연관 컨테이너는 '양방향 반복자'를 제공합니다. 출력 결과를 보면 inorder 방법으로 모든 컨테이너 요소를 탐색하는 것을 알 수 있습니다.

사실 연관 컨테이너에서 모든 요소를 출력하는 것은 별 의미가 없습니다. 연관 컨테이너라는 것이 데이터(key)를 저장하고 저장된 데이터(key)를 빠르게 검색할 목적으로 사용되기 때문입니다. 연관 컨테이너의 검색시간은 '로그 시간'입니다.

 

set은 기본 '비교 함수자'로 less<>를 사용합니다.

아래 예제는 위 예제와 같습니다.

 #include<iostream>
#include<set>
using namespace std;
voidmain( )
{
    set<int , less<int> > s;

    s.insert( 50 );
    s.insert( 10 );
    s.insert( 80 );
    s.insert( 30 );
    s.insert( 70 );
    s.insert( 60 );

    set<int,less<int> >::iterator iter;
    for( iter = s.begin(); iter != s.end() ; iter++)
        cout << *iter << ' ';
    cout << endl;
}
  1. 10 30 50 60 70 80

 set의 두번째 템플릿 인자가 '비교 함수자'입니다. less<>는 operator < 연산자를 중복한 멤버 함수를 갖는 클래스 템플릿입니다. 그래서 트리의 왼쪽 자식으로 작은 데이터(key)값이 오른쪽 자식으로 큰 데이터(key) 값이 위치합니다. 함수자 less<>은 '함수자와 함수 포인터5'에 자세히 설명되어 있습니다.

 

함수자를 greater<> 로 바꾸면 트리의 왼쪽 자식으로 큰 데이터(key)값이 오른쪽 자식으로 큰 데이터(key) 값이 위치합니다.

greater<> 사용 예제

#include<iostream>
#include<set>
using namespace std;

voidmain( )
{
    set<int , greater<int> > s;

    s.insert( 50 );
    s.insert( 10 );
    s.insert( 80 );
    s.insert( 30 );
    s.insert( 70 );
    s.insert( 60 );

    set<int, greater<int> >::iterator iter;
    for( iter = s.begin(); iter != s.end() ; iter++)
        cout << *iter << ' ';
    cout << endl;
}
  1. 80 70 60 50 30 10

 결과는 less<>와 반대로 출력됩니다.

 

이제 연관 컨테이너의 주요 함수인 검색함수를 공부합니다.

 

find() 함수 사용 예제

#include<iostream>
#include<set>
using namespace std;

voidmain( )
{
    set<int> s;

    s.insert( 50 );
    s.insert( 10 );
    s.insert( 80 );
    s.insert( 30 );
    s.insert( 70 );
    s.insert( 60 );

    set<int>::iterator iter;
    iter = s.find( 80 );
    if( iter != s.end() )
        cout << *iter << endl;
    else
        cout << "없음!" << endl;

    iter = s.find( 100 );
    if( iter != s.end() )
        cout << *iter << endl;
    else
        cout << "없음!" << endl;
}
  1. 80
    없음!

 검색한 데이터(key)의 반복자를 반환합니다. 검색한 데이터가 없다면 끝을 표시하는 반복자를 반환합니다.

 

검색하는 내용에는 이 외에도 equal_range()와 lower_bound(), upper_bound()가 있습니다.

이 내용을 공부하기 위해서는 템플릿 클래스 pair<>를 알아야 합니다.

잠시 pair<> 클래스를 공부하고 가겠습니다. 아주 간단합니다.

 

pair<>는 하나의 쌍을 표현하기 위한 클래스입니다. STL의 쌍을 표현하는 모든 곳에서 pair<> 클래스를 사용합니다.

 

사용 예제입니다.

#include<iostream>
#include<string>
using namespace std;

voidmain( )
{
    pair<int , int> p1(10,20);
    cout << p1.first << ',' << p1.second << endl;

    pair<int , string> p2(10, "Hello!" );
    cout << p2.first << ',' << p2.second << endl;

    pair<string, string> p3( "Hello!", "Hi");
    cout << p3.first << ',' << p3.second << endl;
    cout << p3.first.c_str() << endl;
    cout << p3.first.size() << endl;
    cout << p3.second.c_str() << endl;

    cout << p3.second.size() << endl;
}

  1. 10,20
    10,Hello!
    Hello!,Hi
    Hello!
    6
    Hi
    2

 pair<>는 공용 멤버 first와 second를 가지고 있습니다. first는 첫 번째 값을 second는 두 번째 값을 저장하는 변수입니다.

그림으로 간단하게...

pair_객체(1).png 

 

다시 set 컨테이너의 검색 함수를 돌아옵니다.

equal_range() 함수는 찾은 데이터(key)의 구간 반복자를 쌍으로 반환합니다.

사실 set은 저장한 데이터(key)가 유니크하므로(예외?를 제외하고) 반복자 쌍을 사용할 필요는 없지만 multiset이나 multimap에서는 유용하게 사용됩니다. 또 모든 연관 컨테이너는 같은 종류의 멤버 함수들을 제공합니다.

 

equal_range()의 예제입니다.

#include<iostream>
#include<set>
using namespace std;

voidmain( )
{
    set<int> s;

    s.insert( 50 );
    s.insert( 10 );
    s.insert( 80 );
    s.insert( 30 );
    s.insert( 70 );
    s.insert( 60 );

    pair< set<int>::iterator, set<int>::iterator> iter_pair;
    iter_pair = s.equal_range(50);

    if( (iter_pair.first) == s.end() && (iter_pair.second == s.end()) )
        cout << "없음!" << endl;
    else
        cout << *iter_pair.first << ',' << *iter_pair.second << endl; //주의

    iter_pair = s.equal_range(100);
    if( (iter_pair.first) == s.end() && (iter_pair.second == s.end()) )
        cout << "없음!" << endl;
    else
        cout << *iter_pair.first << ',' << *iter_pair.second << endl; //주의
}
  1. 50,60
    없음!

 equal_range()는 찾음 데이터의 시작을 가리키는 반복자와 끝은 가리키는 반복자를 반환합니다.

여기서 주의할 점이 있습니다. set의 경우 찾은 데이터는 first 멤버가 되고 찾은 데이터 다음이 second가 됩니다. 이것은 lower_bound와 upper_bound에 해당합니다.

 

 그림으로 보면...

set_equal_range_함수(1).png 

 

그래서 만약 마지막 데이터인 80을 검색하면 그림은 아래와 같습니다.

 set_equal_range_함수2(2).png

 

lower_bound와 upper_bound 예제입니다.

#include<iostream>
#include<set>
using namespace std;

voidmain( )
{
    set<int> s;

    s.insert( 50 );
    s.insert( 10 );
    s.insert( 80 );
    s.insert( 30 );
    s.insert( 70 );
    s.insert( 60 );

    set<int>::iterator iter_lower;
    iter_lower = s.lower_bound(50);

    if( iter_lower == s.end() )
        cout << "없음!" << endl;
    else
        cout <<"lower: " << *iter_lower<< endl; 

    set<int>::iterator iter_upper;
    iter_upper = s.upper_bound(50);
    if( iter_upper == s.end() )
        cout << "없음!" << endl;
    else
        cout <<"upper: " << *iter_upper<< endl; //주의
}
  1. lower: 50
    upper: 60

 lower_bound()와 upper_bound() 함수 모두 찾은 데이터(key) 위치의 반복자를 반환합니다.

여기서 주의할 점은 lower_bound()은 찾은 데이터 위치의 반복자지만 upper_bound()은 찾은 데이터 위치의 다음 요소를 가리키는 반복자입니다.

 그림으로 보면...

 

set_lower,upper함수.png 

 

지금까지 배운 내용은 모두 multiset에도 적용됩니다.

multiset이 set과 다른 점은 같은 값의 데이터(key)를 저장할 수 있다는 것입니다.

 

간단한 multiset의 출력입니다.

#include<iostream>
#include<set>
using namespace std;
voidmain( )
{
    multiset<int> s;

    s.insert( 50 );
    s.insert( 10 );
    s.insert( 80 );
    s.insert( 80 );
    s.insert( 80 );
    s.insert( 30 );
    s.insert( 50 );
    s.insert( 70 );
    s.insert( 60 );

    multiset<int>::iterator iter;
    for( iter = s.begin(); iter != s.end() ; iter++)
        cout << *iter << ' ';
    cout << endl;
}
  1. 10 30 50 50 60 70 80 80 80

 50과 80 데이터가 중복으로 저장됩니다.

 

여기서 주요 함수가 equal_range()입니다.

#include<iostream>
#include<set>
using namespace std;

voidmain( )
{
    multiset<int> s;

    s.insert( 50 );
    s.insert( 10 );
    s.insert( 80 );
    s.insert( 80 );
    s.insert( 80 );
    s.insert( 30 );
    s.insert( 50 );
    s.insert( 70 );
    s.insert( 60 );

    multiset<int>::iterator iter;
    for( iter = s.begin(); iter != s.end() ; iter++)
        cout << *iter << ' ';
    cout << endl;

    pair<multiset<int>::iterator, multiset<int>::iterator > iter_pair;
    iter_pair = s.equal_range( 80 );
    for( iter = iter_pair.first ; iter != iter_pair.second ; iter++)
        cout << *iter << ' ';
    cout << endl;
}
  1. 10 30 50 50 60 70 80 80 80
    80 80 80

 equal_range() 함수를 사용하면 찾은 데이터의 구간 반복자 쌍을 얻을 수 있습니다.

설명은 그림으로...

 

 multiset_equal_range함수.png

 

 

2, typedef 내장 자료형

모든 컨테이너 클래스에는 typedef되어 있는 자료형들을 제공합니다. 이 자료형중 연관 컨테이너처럼 '비교 함수자'를 가지는 컨테이너는 key_comp()라는 함수를 제공합니다.

key_comp()함수를 사용하면 우리가 2진 트리를 정렬하기 위해 지정했던 함수자(기본 함수자 less<>) 객체를 리턴합니다.

 

key_comp()함수 예제

#include<iostream>
#include<set>
using namespace std;

voidmain( )
{
    multiset<int> s;

    s.insert( 50 );
    s.insert( 10 );
    s.insert( 80 );
    s.insert( 30 );
    s.insert( 70 );
    s.insert( 60 );

    less<int> key_less = s.key_comp();
    if( key_less( 10, 20 ) )
        cout << "true" << endl;
    else
        cout << "false" << endl;
    if( s.key_comp()( 10, 20) )
        cout << "true" << endl;
    else
        cout << "false" << endl;
}
  1. true
    true

 key_comp()함수가 less<> 객체를 리턴하므로 함수를 사용하여 지정했던 함수자 객체를 반환받거나 임의의 데이터를 비교할 수 있습니다.

 

greater<>를지정한 key_comp()함수 예제

#include<iostream>
#include<set>
using namespace std;

voidmain( )
{
    multiset<int, greater<int> > s;

    s.insert( 50 );
    s.insert( 10 );
    s.insert( 80 );
    s.insert( 30 );
    s.insert( 70 );
    s.insert( 60 );

    greater<int> key_greater = s.key_comp();
    if( key_greater( 10, 20 ) )
        cout << "true" << endl;
    else
        cout << "false" << endl;
    if( s.key_comp()( 10, 20) )
        cout << "true" << endl;
    else
        cout << "false" << endl;
}
  1. false
    false

 greater<>를 사용하므로 결과는 false 입니다.

'Programming > C++/STL' 카테고리의 다른 글

STL :: 컨테이너(map, multimap)  (0) 2011/01/08
STL :: 컨테이너(set, multiset)  (0) 2011/01/08
STL :: 컨테이너(map, multimap)  (0) 2011/01/08
STL :: 컨테이너(deque)  (0) 2011/01/08
STL :: 컨테이너(string)  (0) 2011/01/08
STL :: 컨테이너(list)  (0) 2011/01/08
Posted by 전산쟁이폴
STL :: 컨테이너(deque)

출처 :
http://blog.daum.net/coolprogramming/77

deque 컨테이너는 '시퀀스 컨테이너'이며 '연속 메모리 기반 컨테이너'입니다.

연속 메모리 기반 컨테이너의 특징이 필요하고 요소의 추가, 삭제가 앞쪽과 뒤쪽에서 빈번하게 발생한다면 deque를 사용하는 것이 좋습니다.

 

deque의 주요 개념을 그림으로 표현하면

deque_개념.png 

그림에서 보는 것처럼 deque는 앞과 뒤에 데이터 요소들이 추가될 수 있는 형태입니다. 또 데이터 요소를 저장하는 여러 개의 메모리 단위를 갖습니다.

그래서 요소의 추가, 삭제가 앞쪽과 뒤쪽에서 빈번하게 발생하더라도 vector나 string 처럼 메모리를 재할당하고 컨테이너의 모든 요소를 복사할 필요 없이 새로운 메모리 단위를 할당하여 새 요소만 추가하면 됩니다. 또 컨테이너의 중간 요소가 삽입(insert), 삭제(erase) 되더라도 요소들을 뒤쪽이나 앞쪽으로 모두 밀어낼 수 있으므로 vector나 string 보다 더 좋은 성능을 갖습니다. 위 그림을 이해한다면 그리 어렵지 않게 알 수 있을 것입니다.

 

1, deque의 특징과 주요 함수

 앞, 뒤로 추가, 삭제될 수 있으므로 push_front(), push_back(), pop_front(), pop_back()함수 등을 가지고 있으며 연속 메모리 기반 컨테이너이므로 []연산자 함수를 제공합니다.

 

데이터 추가와 출력

#include<iostream>
#include<deque>
using namespace std;
voidmain( )
{
    deque<int> dq;

    for(int i = 0 ; i < 10 ; i++)
        dq.push_back( (i+1)*10 );

    for( int i = 0 ; i < dq.size() ; i++)
        cout << dq[i] << ' ';
    cout << endl;
}
  1. 10 20 30 40 50 60 70 80 90 100

 데이터를 추가하더라도 일정한 메모리 단위가 생성되며 요소들이 추가됩니다.

위 예제의 그림입니다. 새로운 메모리 단위만 생성할 뿐 vector처럼 메모리 재할당 및 모든 요소 복사가 발생하지 않습니다.

deque_요소_추가(1).png 

 

 

앞쪽에 데이터 요소를 추가하더라도 효율적으로 메모리를 생성합니다.

#include<iostream>
#include<deque>
using namespace std;

voidmain( )
{
    deque<int> dq;

    for(int i = 0 ; i < 10 ; i++)
        dq.push_back( (i+1)*10 );

    for( int i = 0 ; i < dq.size() ; i++)
        cout << dq[i] << ' ';
    cout << endl;

    dq.push_front(100);
    dq.push_front(200);
    dq.push_front(300);
    for( int i = 0 ; i < dq.size() ; i++)
        cout << dq[i] << ' ';
    cout << endl;
}
  1. 10 20 30 40 50 60 70 80 90 100
    300 200 100 10 20 30 40 50 60 70 80 90 100

 push_front()로 앞쪽에 데이터 요소를 추가하면 새로운 메모리 단위를 생성하고 데이터를 추가합니다.

위 예제의 그림입니다.

 deque_요소_추가_front.png

 

 

또 컨테이너 요소 중간에 삽입(insert), 삭제(erase) 하더라도 데이터 요소의 개수가 작은 쪽(앞쪽, 뒤쪽)을 밀어냅니다. vector 처럼 메모리가 부족하면 재할당하고 모든 요소를 복사할 필요없이 새로운 메모리 단위만 할당하면 됩니다.

간단한 insert() 예제입니다.

#include<iostream>
#include<deque>
using namespace std;

voidmain( )
{
    deque<int> dq;

    for(int i = 0 ; i < 10 ; i++)
        dq.push_back( (i+1)*10 );

    for( int i = 0 ; i < dq.size() ; i++)
        cout << dq[i] << ' ';
    cout << endl;

    deque<int>::iterator iter = dq.begin()+1;

    iter = dq.insert( iter , 500);


    for( int i = 0 ; i < dq.size() ; i++)
        cout << dq[i] << ' ';
    cout << endl;
    cout << *iter << endl;
}


  1. 10 20 30 40 50 60 70 80 90 100
    10 500 20 30 40 50 60 70 80 90 100
    500

 중간에 데이터 요소를 삽입하면 삽입할 위치를 기준으로 앞쪽 요소의 개수가 작으므로 앞쪽으로 밀어냅니다. 삽입은 항상 반복자가 가리키는 위치 앞에 삽입됩니다. erase도 비슷하게 동작합니다.

위 예제의 그림입니다.

 

deque_요소_삽입.png 

 

 

 2, deque의 반복자

deque는 '연속 메모리 기반 컨테이너'이므로 임의 접근 반복자를 제공합니다.

 

반복자 예제

#include<iostream>
#include<deque>
using namespace std;

voidmain( )
{
    deque<int> dq;

    for(int i = 0 ; i < 10 ; i++)
        dq.push_back( (i+1)*10 );

    deque<int>::iterator iter;
    for( iter = dq.begin(); iter != dq.end() ; iter++)
        cout << *iter << ' ';
    cout << endl;

    iter = dq.begin() + 3;
    cout << *iter << endl;
    iter -= 3;
    cout << *iter << endl;
}
  1. 10 20 30 40 50 60 70 80 90 100
    40
    10

 deque도  vector, string처럼 '임의 접근 반복자'를 제공합니다.

'Programming > C++/STL' 카테고리의 다른 글

STL :: 컨테이너(set, multiset)  (0) 2011/01/08
STL :: 컨테이너(string)  (0) 2011/01/08
STL :: 컨테이너(deque)  (0) 2011/01/08
STL :: 컨테이너(string)  (0) 2011/01/08
STL :: 컨테이너(list)  (0) 2011/01/08
STL :: 컨테이너(vector)  (0) 2011/01/08
Posted by 전산쟁이폴
STL :: 컨테이너(string)

출처 :
http://blog.daum.net/coolprogramming/77

STL의 문자열을 다루는 전용 컨테이너가 string입니다. 사실 string은 basic_string<char>의 typedef일 뿐입니다.

 

문자열을 다루는 컨테이너는 string과 wstring 두 가지입니다.

  • string은 basic_string<char>의 typedef입니다.
  • wstring은 basic_string<wchar_t>의 typedef입니다.

 

간단하게 말해서 char는 ASCII 문자셋(8비트 문자셋)을 위한 타입이며 wchar_t는 Wide Byte Character Set(WBCS:16비트 문자셋)을 위한 타입입니다. 이것은 윈도우즈 기준이며 문자셋에 대한 내용은 다음에 공부할 기회가 있을 것입니다.(SBCS, DBCS, MBCS, WBCS)

 

그래서 string과 wstring은 저장 바이트 수만 다를 뿐 모든 멤버와 기능은 같습니다.(basic_string<>의 typedef일 뿐이므로)

우리는 char형태의 문자를 저장하는 string을 공부합니다.

 

string은 '시퀀스 컨테이너'이며 '연속 메모리 기반 컨테이너'입니다. vector와 비슷한 특징을 갖습니다.

string의 주요 개념을 그림으로 표현하면

string_개념.png 

 

 

1, string의 특징과 주요 함수

  string은 문자열 전용 컨테이너로 문자열의 추상화 클래스입니다. 그래서 문자열을 쉽게 처리할 수 있는 여러 가지 기능을 제공합니다.

 

간단한 문자열 출력 예제.

#include<iostream>
#include<string>
using namespace std;
voidmain( )
{  
    string str = "Hello!";

    cout << str << endl;
}
  1. Hello!

 << 연산자가 중복돼 있으므로 Hello!을 출력합니다.

 

연속 메모리 기반 컨테이너의 특징은 [] 연산자 함수도 가지고 있습니다.

#include<iostream>
#include<string>
using namespace std;

voidmain( )
{  
    string str;

    str.push_back('H');
    str.push_back('e');
    str.push_back('l');
    str.push_back('l');
    str.push_back('o');
    str.push_back('!');

    cout << str << endl;

    for(int i = 0 ; i < str.size() ; i++)
        cout << str[i] ;
    cout << endl;
}
  1. Hello!
    Hello! 

 str[i]가 가능합니다.

 

'연속 메모리 기반 컨테이너'이므로 string은 vector처럼 임의 접근 반복자를 제공합니다.

 #include<iostream>
#include<string>
using namespace std;
voidmain( )
{  
    string str;

    str.push_back('H');
    str.push_back('e');
    str.push_back('l');
    str.push_back('l');
    str.push_back('o');
    str.push_back('!');

    string::iterator iter;
    for( iter = str.begin() ; iter != str.end() ; iter++)
        cout << *iter;
    cout << endl;

    iter = str.begin() + 2;
    cout << *iter << endl;
    iter += 2;
    cout << *iter << endl;
    iter -= 4;
    cout << *iter << endl;
}
  1. Hello!
    l
    o
    H

 임의 접근 반복자는 산술연산이 가능합니다.

vector와 비슷한 특징을 갖습니다.

#include<iostream>
#include<string>
using namespace std;

voidmain( )
{  
    string str;

    cout << "size : capacity " << endl;
    for(int i = 0 ; i < 26  ; i++)
    {
        str.push_back('A'+i);
        cout << str.size() <<":" << str.capacity() << endl;
    }
}
  1. size : capacity
    1:15
    2:15
    3:15
    4:15
    5:15
    6:15
    7:15
    8:15
    9:15
    10:15
    11:15
    12:15
    13:15
    14:15
    15:15
    16:31
    17:31
    18:31
    19:31
    20:31
    21:31
    22:31
    23:31
    24:31
    25:31
    26:31

 메모리를 확장하는 정책이 조금 다를 뿐입니다.

 

문자열은 문자들의 추가가 빈번하게 발생할 수 있으므로 reserve한 정확한 크기만큼 할당하지 않고 메모리 확장 정책에 따라 일정한 크기만큼 메모리 공간을 확보합니다.

#include<iostream>
#include<string>
using namespace std;

voidmain( )
{  
    string str;

    str.reserve(26);

    cout << "size : capacity " << endl;
    for(int i = 0 ; i < 26  ; i++)
    {
        str.push_back('A'+i);
        cout << str.size() <<":" << str.capacity() << endl;
    }
}
  1. size : capacity
    1:31
    2:31
    3:31
    4:31
    5:31
    6:31
    7:31
    8:31
    9:31
    10:31
    11:31
    12:31
    13:31
    14:31
    15:31
    16:31
    17:31
    18:31
    19:31
    20:31
    21:31
    22:31
    23:31
    24:31
    25:31
    26:31

 26만큼의 메모리를 예약하지만 실제 메모리 확장 정책에 따라 31크기만큼 확보합니다.

 

2, 문자열 관련 함수

문자열 관련해서 여러 가지 함수를 제공합니다.

 

입력 스트림도 지원합니다.

#include<iostream>
#include<string>
using namespace std;

voidmain( )
{  
    string str;

    cin >> str ; // Hello! 입력
    cout << str << endl;
}
  1. Hello!

 >> 연산자가 중복되어 있습니다.

 

 

기본적인 문자열 함수.

 #include<iostream>
#include<string>
using namespace std;
voidmain( )
{  
    string str1= "Hello!";
    string str2;

    str2 = str1;
    cout << str1 << " , " << str2 << endl;
    if( str1 == str2 )
        cout << "true" << endl;
    str2 += str1;
    cout << str2 << endl;
    str2 = str1.substr(0, 2);
    cout << str2 << endl;
    cout << str1.find('e') << endl;
}
  1. Hello! , Hello!
    true
    Hello!Hello!
    He
    1

 쉽게 알 수 있습니다.

 

또 string 컨테이너는 char * 관련 문자열 함수를 지원합니다.

#include<iostream>
#include<string>
using namespace std;

voidmain( )
{  
    string str = "Hello!";

    constchar * s1 = str.c_str();
    cout << s1 << endl;

    char buf[100]={0};
    str.copy(buf, 6);
    cout << buf << endl;

}

  1. Hello!
    Hello!

 c_str()함수는 '\0'문자를 포함한 문자열을 리턴합니다. 버퍼에 문자열을 받아오는 copy()함수도 지원합니다.

 

이외에 string은 '임의 접근 반복자'를 지원하므로 임의 접근 반복자를 사용하는 여러 알고리즘을 이용할 수 있습니다.

'Programming > C++/STL' 카테고리의 다른 글

STL :: 컨테이너(set, multiset)  (0) 2011/01/08
STL :: 컨테이너(deque)  (0) 2011/01/08
STL :: 컨테이너(string)  (0) 2011/01/08
STL :: 컨테이너(deque)  (0) 2011/01/08
STL :: 컨테이너(list)  (0) 2011/01/08
STL :: 컨테이너(vector)  (0) 2011/01/08
Posted by 전산쟁이폴
STL :: 컨테이너(list)

출처 : http://blog.daum.net/coolprogramming/77

list는 '시퀀스 컨테이너'이며 '노드 기반 컨테이너'입니다. 그래서 데이터의 삽입, 삭제가 시퀀스 중간에 자주 발생할 때 사용하면 좋은 컨테이너입니다.

 

list의 주요 개념을 그림으로 표현하면

list_컨테이너.png 

 

1, list의 반복자

위 그림처럼 list는 앞쪽과 뒤쪽 모두에 데이터를 추가(push_front(), push_back())할 수 있는 함수들을 제공합니다. 또 list는 이중 연결 리스트로 구현되어 있어 앞뒤로 이동 가능한 '양방향 반복자'를 제공합니다. 연결 리스트의 특징인 중간에 삽입, 삭제가 빈번하게 발생할 때 효율적인 컨테이너입니다.

 

list 간단한 예제(데이터 앞쪽, 뒤쪽 추가)

 #include<iostream>
#include<list>
using namespace std;

voidmain( )
{
    list<int> lt;


    lt.push_back(10); // 리스트 뒤쪽에 추가
     lt.push_back(20);
    lt.push_back(30);
    lt.push_back(40);
    lt.push_back(50);

    list<int>::iterator iter;
    for( iter = lt.begin() ; iter != lt.end() ; iter++)
        cout << *iter << ' ';
    cout << endl;

    lt.push_front(60); // 리스트 앞쪽에 추가
     lt.push_front(70);
    lt.push_front(80);
    for( iter = lt.begin() ; iter != lt.end() ; iter++)
        cout << *iter << ' ';
    cout << endl;

}

  1. 10 20 30 40 50
    80 70 60 10 20 30 40 50

 list는 데이터를 앞뒤에 추가할 수 있는 '시퀀스 컨테이너'로 push_front()와 push_back()함수를 제공합니다.(push_front()를 제공하는 컨테이너는 list와 deque뿐입니다.)

10~50까지 리스트 뒤쪽에 추가되며 60, 70, 80은 계속 앞쪽에 추가됩니다.

list_컨테이너_추가(1).png 

 

 

  list는 '연속 메모리 기반 컨테이너'가 아니므로 operator[]연산자는 가지고 있지 않고 반복자를 통해서만 컨테이너의 요소들에 접근할 수 있습니다. list는 양방향 반복자를 제공합니다. 그래서 반복자에 산술 연산은 불가능하다.(산술 연산이 가능한 반복자는 '임의 접근 반복자'뿐입니다. '연속 메모리 기반 컨테이너'가 임의 접근 반복자를 제공합니다.) 8개의 STL 컨테이너 모두 양방향 반복자 이상을 제공하며 이 중 '연속 메모리 기반 컨테이너'인 string, vector, deque는 산술 연산이 가능한 임의 접근 연산자를 제공합니다.

 

다음은 양방향 반복자를 사용하는 예제입니다.

#include<iostream>
#include<list>
using namespace std;
voidmain()
{
    list<int> lt;


    lt.push_back(10);

    lt.push_back(20);
    lt.push_back(30);
    lt.push_back(40);
    lt.push_back(50);

    list<int>::iterator iter;
    for( iter = lt.begin() ; iter != lt.end(); iter++)
        cout << *iter << ' ';
    cout << endl;

    iter--;

// iter += 2; 산술 연산은 불가능

    cout << *iter << endl;
    cout << *--iter << endl;
    cout << *--iter << endl;
    cout << *--iter << endl;
    cout << *--iter << endl;

}

  1. 10 20 30 40 50
    50
    40
    30
    20
    10

 양방향 반복자로 반복자에 ++, -- 연산자 모두 가능하며 산술연산은 불가능합니다.

list_양방향_반복자.png 

 

  모든 STL 컨테이너는 시작과 끝을 가리키는 반복자 쌍을 반환하는 begin()과 end()를 가진다고 했습니다. 또 모든 컨테이너는 이와 반대인 rbegin(), rend() 함수로 끝과 시작을 가리키는 반복자 쌍을 제공합니다.

 

rbegin()과 rend()함수의 사용

#include<iostream>
#include<list>
using namespace std;

voidmain()
{
    list<int> lt;

    lt.push_back(10);

    lt.push_back(20);
    lt.push_back(30);
    lt.push_back(40);
    lt.push_back(50);

    list<int>::iterator iter;
    for( iter = lt.begin() ; iter != lt.end(); iter++)
        cout << *iter << ' ';
    cout << endl;

    list<int>::reverse_iterator riter;
    for( riter = lt.rbegin() ; riter != lt.rend(); riter++)
        cout << *riter <<' ';
    cout << endl;
}

  1. 10 20 30 40 50
    50 40 30 20 10

 list의 뒤에서 앞쪽으로 이동하도록 reverse_iterator를 제공합니다. 이 시작과 끝을 가리키는 반복자 쌍을 반환하는 함수가 rbegin()과 rend()입니다. 반복자의 자세한 내용은 뒤쪽에서 다시 공부합니다.

list_reverse_iterator_반복자.png 

 

2, list의 주요 특징과 함수

  list의 장점 중 하나가 데이터 요소의 삽입, 삭제시에 다른 '시퀀스 컨테이너'보다 '효율적이다'는 것입니다. 이유는 '연속 메모리 기반 컨테이너'처럼 컨테이너 요소들이 밀려나지 않기 때문입니다.

모든 컨테이너는 insert()함수와 erase()함수를 가지고 있어 반복자가 가리키는 위치의 요소를 삽입하거나 삭제할 수 있습니다. 또 STL이 제공하는 여러 삽입, 삭제 알고리즘을 사용할 수 있습니다.

이때 '연속 메모리 기반 컨테이너'는 주의할 점이 있습니다. 연속 메모리 기반 컨테이너는 요소의 삽입과 삭제가 '선형 시간'이라는 것입니다. 또 메모리가 재할당될 수 있고 컨테이너의 모든 다른 요소들이 복사될 수 있다는 것입니다.

 

vector와 list의 삽입 예제

#include<iostream>
#include<list>
#include<vector>
using namespace std;

voidmain()
{
    vector<int> v;
    list<int> lt;

    v.push_back(10);
    v.push_back(20);
    v.push_back(30);
    v.push_back(40);
    v.push_back(50);


    lt.push_back(10);

    lt.push_back(20);
    lt.push_back(30);
    lt.push_back(40);
    lt.push_back(50);

    vector<int>::iterator viter = v.begin();
    viter++;
    viter = v.insert( viter, 80); // vector의 두 번째 요소로 삽입
     cout << *viter << endl;

    list<int>::iterator liter = lt.begin();
    liter++;

    liter = lt.insert( liter, 80); // list의 두 번째 요소로 삽입
      cout << *liter << endl;

    cout << " vector : ";
    for( viter = v.begin() ; viter != v.end(); viter++)
        cout << *viter << ' ';
    cout << endl;

    cout << " list : ";
    for( liter = lt.begin() ; liter != lt.end(); liter++)
        cout << *liter << ' ';
    cout << endl;
}

  1. 80
    80
     vector : 10 80 20 30 40 50
     list : 10 80 20 30 40 50

 결과는 같지만 내부적인 동작은 전혀 다릅니다. vector의 요소는 밀려나며 메모리 재할당 및 복사가 발생할 수 있습니다.

아래는 위 예제의 그림입니다.

vector의_삽입.png

list의_삽입.png 

 

아래는 erase()함수 예제입니다.

#include<iostream>
#include<list>
#include<vector>
using namespace std;

voidmain()
{
    vector<int> v;
    list<int> lt;

    v.push_back(10);
    v.push_back(20);
    v.push_back(30);
    v.push_back(40);
    v.push_back(50);


    lt.push_back(10);    lt.push_back(20);
    lt.push_back(30);
    lt.push_back(40);
    lt.push_back(50);

    vector<int>::iterator viter = v.begin();
    viter++;
    viter = v.erase( viter); // vector의 두 번째 요소로 삭제
     cout << *viter << endl;

    list<int>::iterator liter = lt.begin();    liter++;
    liter = lt.erase( liter); // list의 두 번째 요소로 삭제
     cout << *liter << endl;

    cout << " vector : ";
    for( viter = v.begin() ; viter != v.end(); viter++)
        cout << *viter << ' ';
    cout << endl;

    cout << " list : ";
    for( liter = lt.begin() ; liter != lt.end(); liter++)
        cout << *liter << ' ';
    cout << endl;
}

  1. 30
    30
     vector : 10 30 40 50
     list : 10 30 40 50

 erase()함수를 사용한 삭제도 '삽입' 동작과 같은 특징을 보입니다.

'Programming > C++/STL' 카테고리의 다른 글

STL :: 컨테이너(deque)  (0) 2011/01/08
STL :: 컨테이너(string)  (0) 2011/01/08
STL :: 컨테이너(list)  (0) 2011/01/08
STL :: 컨테이너(vector)  (0) 2011/01/08
STL :: 표준 C++ 라이브러리  (0) 2011/01/04
C++,STL :: vector 사용하기  (0) 2010/10/26
Posted by 전산쟁이폴