728x90

DirectX 11에서는 Resource에 데이터를 넣는 함수는 4개가 존재한다.


UpdateSubresource(), Map() ~ Unmap(), CopyResource(), CopySubresourceRegion()


이 함수들은 자주 사용되는 만큼 어느 정도는 알고 사용해야 할 것 같아서 정리한다.




우선 함수의 속도를 보면 UpdateSubresource()는 웬만하면 매 프레임마다 사용하지 않는 것이 좋다는 생각이 들 것이다.



CPU -> GPU로 데이터를 복사할 때 사용하는 함수는 UpdateSubresource()와 Map() ~ Unmap()을 사용하게 된다. 


Map() ~ Unmap()의 경우 개발자가 직접 Lock() ~ Unlock()을 하며 memcpy()를 이용해서 Resource에 데이터를 직접 넣는 방법이고, UpdateSubresource()는 함수 호출만으로 내부에서 Lock() ~ Unlock()을 해주기 때문에 나의 경우 UpdateSubresource()를 더 즐겨 사용하는 편이다.


그리고 기본적으로 Lock() ~ Unlock()이 걸리게 되므로, 하나의 Resource에 Thread를 이용하여 동시에 CPU -> GPU로 복사는 불가능하다.



GPU -> GPU로 데이터를 복사할 때 사용하는 함수는 CopyResource()와 CopySubresourceRegion()가 있다. 


이 함수의 차이는 CopyResource()는 Full Image를 그대로 복사하고, CopySubresourceRegion()는 영역을 지정하여 복사가 가능한 것이다.


이 두개의 함수는 기본적으로 비동기 함수이다. 그렇기 때문에 하나의 Resource에 Thread를 이용하여 GPU -> GPU 복사는 얼마든지 가능하다.


테스트를 해보았을 때에는 Frame Rate는 거의 차이가 없었다.



국내 사이트에서는 자료가 없기에 해외 사이트를 검색하여 얻은 자료를 토대로 테스트 결과를 정리해보았다.


참조 사이트 : https://eatplayhate.me/2013/09/29/d3d11-texture-update-costs/

728x90
728x90

C++에서 함수 포인터는 함수자(functor)를 구현할 때 유용하게 사용되며 STL이나 콜백 메커니즘을 구현할 때도 유용하게 사용될 수 있습니다.

 

1, 함수 포인터의 종류

C++언어의 함수는 세 가지 방식으로 호출할 수 있습니다.

  • 첫째, 정적 함수 호출(전역함수, 클래스 내의 정적함수, 네임 스페이스 내의 전역함수등)
  • 둘째, 객체로 멤버 함수 호출(객체 자체로 호출)
  • 셋째, 객체의 주소로 멤버 함수 호출(객체의 주소로 호출)

위 세 가지에 대한 함수 포인터 선언및 호출 방식이 다릅니다.

 

우리는 이미

첫째, 정적 함수의 주소를 저장하는 함수 포인터는 알고 있습니다.

 

여기서 다시 복습해 볼까요?

#include <iostream>
using namespace std;
class Point
{
    int x, y;
public:
    // ... 여러 멤버들 ..
    static void Print(int n) // 2.Point 클래스 static 함수
    {
        cout << "n = " << n << endl;
    }
};

void Print(int n) // 1.전역 함수
{
    cout << "n = " << n << endl;
}

namespace NetGong
{
    void Print(int n) // 3.NetGong 네임스페이스  내의 전역함수
    {
        cout << "n = " << n << endl;
    }
}

void main( )
{
    void (*pfunc)(int ); // 함수 포인터 선언 - C언어 QuickStart1 참고
   
    pfunc = &Print; // 1.전역함수 주소 저장
    pfunc( 10 ); // 1.전역함수 호출

    pfunc = &Point::Print; // 2.Point 클래스 static 함수 저장
    pfunc( 10 ); // 2.Point 클래스 static 함수 호출

    pfunc = &NetGong::Print; // 3.NetGong 네임스페이스  내의 전역함수 저장
    pfunc( 10 ); // 3.NetGong 네임스페이스  내의 전역함수 호출
}
  1. n = 10
    n = 10
    n = 10

뭐.. 별로 설명할 것이 없습니다. 

알아 둘 것은 위 세 가지 경우 모두 같은 포인터 변수를 사용한다는 것입니다. 호출 방식이 같거든요.

함수의 주소를 저장할 때 Print가 함수 주소이므로 &Print 와 같이 &를 붙여도 그만 붙이지 않아도 그만이지만 명시적으로 &붙여 사용합니다. 연산자의 위치와 용도에 따라 의미가 달라지는 걸 아시죠? 이곳에서 &가 있든 없든 같은 코드입니다. 또, 아래쪽에서 배울 맴버함수는 꼭 & 연산자가 있어야 합니다. 하여튼 &를 붙이는 방식을 선호하므로 꼭 붙여서 사용하는 습관을 들이세요.

 

이제 둘째, 셋째 멤버 함수호출인 멤버 함수 포인터를 사용하는 예제를 공부해 보도록 하겠습니다.

  • 멤버 함수 포인터 선언

 class Point
{

//멤버 ...
    void Print( )
    {
        cout << "(" << x << "," << y << ")" << endl;
    }
};

 void (Point::*pfunc)( ); //멤버 함수 주소를 저장하기 위한 함수 포인터 선언

 일반 함수 포인터라면 void (*pfunc)( ); 라고 선언했겠지만 Point 클래스의 멤버 함수 포인터이므로 Point::* 와 같은 연산자를 사용합니다.

  • 멤버 함수 포인터를 이용한 호출

    • 객체로 호출 : (객체.*pfunc)( );
    • 객체의 주소로 호출 : (객체주소->*pfunc)( );

.* 연산자와 ->*연산자를 이용하여 각각 객체와 객체의 주소로 함수 포인터가 가리키는 멤버함수를 호출할 수 있습니다. 또 연산자 우선순위 때문에 ( )로 묶어 주어야합니다. 주의하세요.

 

2, 멤버 함수 포인터 실습

그럼 실제 함수 포인터 예제를 보도록 하겠습니다.

#include <iostream>
using namespace std;

class Point
{
    int x, y;
public:
    Point(int _x = 0, int _y = 0) : x(_x), y(_y) { }
    void Print( )
    {
        cout << "(" << x << "," << y << ")" << endl;
    }
};

void main( )
{
    Point d1(2,3);
    Point *p = &d1;

    void (Point::*pfunc)( ); //멤버 함수 주소를 저장하기 위한 함수 포인터 선언
    pfunc = &Point::Print; 

    d1.Print(); //둘째, 객체로 멤버 함수 호출
    p->Print();  //셋째, 객체의 주소로 멤버 함수 호출

    (d1.*pfunc)( ); //둘째, 객체로 함수 포인터 이용한 멤버 함수 호출
    (p->*pfunc)( ); //셋째, 객체의 주소로 함수 포인터 이용한 멤버 함수 호출

    Point d2, d3(5,5);
    (d2.*pfunc)();
    (d3.*pfunc)();
}
  1. (2,3)
    (2,3)
    (2,3)
    (2,3)
    (0,0)
    (5,5)

객체는 연산자 .*를 사용하고 객체의 주소는 ->* 연산자를 사용한다는 것만 주의하세요.

 

또 다른 예제를 보겠습니다.

#include <iostream>
using namespace std;
class Point
{
    int x, y;
public:
    Point(int _x = 0, int _y = 0) : x(_x), y(_y) { }
    bool Compare(const Point& arg)
    {
        return x==arg.x && y == arg.y ? true : false ;
    }
    void Print( )
    {
        cout << "(" << x << "," << y << ")" << endl;
    }
};

void main( )
{
    Point d1(2,3);
    Point d2(5,5);
    Point *p = &d1;

    bool (Point::*pfunc)(const Point& ); //멤버 함수 주소를 저장하기 위한 함수 포인터 선언
    pfunc = &Point::Compare;

    cout << d1.Compare(d2) << endl; //둘째, 객체로 멤버 함수 호출
    cout << p->Compare(d1) << endl; //셋째, 객체의 주소로 멤버 함수 호출

    //둘째, 객체로 함수 포인터 이용한 멤버 함수 호출
    cout << (d1.*pfunc)(d2) << endl;
    //셋째, 객체의 주소로 함수 포인터 이용한 멤버 함수 호출
    cout << (p->*pfunc)(d1) << endl;
}
  1. 0
    1
    0
    1

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

728x90
728x90

Doxygen은 주석이 달린 코드를 읽어내서 문서를 만들어내는 툴이며, 여러가지 언어와 문서형식(MS-WORD or HTML...) 지원한다.

우리는 C++ 코드를 HTML문서로 뽑아내는 일을 해볼 것이다.

 

 

그럼 우선 Doxygen을 다운받아보자.

우선 Windows 계열의 경우 이 주소를 통해 다운받으면 된다.

http://ftp.stack.nl/pub/users/dimitri/doxygen-1.8.7-setup.exe

 

그외의 OS에서는 아래부분에서 자신의 OS와 맞는 파일을 받아주자.

http://www.stack.nl/~dimitri/doxygen/download.html



다운을 다 받았으면 파일을 실행시키고 동의를 해주거나, NEXT NEXT만 눌러주면 된다.

다운로드를 모두 마치고 나면 DoxyWizard란 프로그램을 실행시켜보자.




그럼 이렇게 생긴 프로그램이 켜질것이다.

대강 프로젝트 이름(Project name)이나 시놉시스, 버전등을 적어주자.

 

 

그다음으로, 주석이 한글일 경우에 인코딩문제로 인해 깨지는걸 방지하기위해 추가적으로 설정을 해주어야 한다.

Expert탭에가서 Topics중에 Project에 가보자.




여기서 아랫부분의 OUTPUT_LANGUAGE부분을 English에서 이미지에서와 같이 Korean으로 바꿔주어야 한다.

 

 

추가적으로 Topics중에서 Input탭으로 가서 INPUT_ENCODING부분에 CP949를 적어주자.




위의 절차대로 설정을 했다면 이제 준비는 모두 끝났다.

다음으로 읽어들일 간단한 소스를 짜볼 건데, 그 전에 규격에 맞게 주석을 다는 방법에 대해 알아보자.

 

클래스나 멤버변수의 위에 

/**

 *@종류 내용

 *@종류 내용

*/

이런방식으로 주석을 달아주면 된다.

종류에는 이러한 것들이 있다.

Item설명
@author작성자 이름
@brief간략한 설명
@bug발견된 버그의 설명
@code중요 코드를 설명할 때 시작 지점 설정
@date작성날짜
@endcode중요코드 설명할 때 종료 지점 설정
@exception예외 처리
@mainpage
@param함수 파라미터
@remark자세한 설명 
@return함수의 리턴 값
@section
@see참고할 함수나 페이지 지정
@todo해야할 일의 설명

 

출처 : http://rorolena.tistory.com/2176

 

그리고 위의 코드처럼 간략설명 하나만 적으려는데 길게 코드를 적는게 맘에들지 않는다면 이렇게 쓸 수 있다.

 

//! 간략한 설명 내용

 

위의것이 아랫것과 같다고 할까.

 

/**

 *@brief 내용

*/

 

그럼 이제 예제코드를 봐보자.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#include <iostream>
#include <string>
 
/**
 *@brief Doxygen 공부용 예제 클래스
 *@author 이민욱
 *@date 2014/05/03
*/
class Test
{
public:
    /**
     *@brief 객체가 생성됬을 때의 시간
     *@author 이민욱
    */
    int Timer ; 
 
    //! 공격력
    int Power ;
 
    //! 객체의 이름
    std::string m_Name ;
} ;

 

 

Test클래스에 brief, 간략설명으로 Doxygen 공부용 예제 클래스를 적어주었고, 그외 추가로 작성자나 작성된 날짜 정보를 적어주었다.

그리고 멤버변수들에도 각기 주석들을 달아 주었다.

 

이렇게 코드 작성을 했으면 이제 다시 Doxygen Wizard로 가보자.

그리고 Wizard탭에 가서, Source code directory(소스코드의 위치)와 Destination directory(결과물 출력 폴더)를 설정해주자.





설정을 끝마쳤으면, Run탭에가서 Run doxygen버튼을 눌러주면 끝이다.

잠시후 기다리면 영어들이 줄줄이 나오다가 *** Doxygen has finished란 말이 나올 것이다.





이제 방금 설정했던 결과물 출력 폴더로 가보면 latex폴더와 html폴더가 만들어져있는데, html폴더로 들어간 후, Index.html 파일을 브라우저를 이용하여 열어보자.

그러면 이렇게 html문서가 보기좋게 만들어진 것을 볼 수 있을 것이다.




출처 : http://blog.naver.com/minuk302/130190474760









728x90

'Dev Tool' 카테고리의 다른 글

Dev Tool - TortoiseSVN  (0) 2017.02.23
Dev Tool - depends22  (0) 2016.10.14
Dev Tool - StarUML  (0) 2016.02.24
Dev Tool - Beyond Compare  (0) 2016.02.24
Dev Tool - Debug View  (0) 2016.02.24
728x90

이번 장은 앞쪽 포인터부분 공부가 필수적입니다. 앞쪽 포인터부분을 이해했다면 이 장은 그냥 덤입니다. ㅡㅡ;;

1, void 포인터

모든 변수는 변수의 크기와 메모리  저장형식인 자료형이라는 것이 존재합니다. C언어에서 void는 '자료형이 없다', '자료형이 정해지지 않았다'라는 의미로 사용됩니다. 그렇다면 int n; 처럼 void v; 이 가능할까요? 당근 불가합니다. 다시말하지만(변수장에서 했습니다.) 자료형이란 변수의 연산, 크기, 저장형태을 나타낸다고 했습니다. void는 자료형이 없다는 의미이므로 변수를 만들 수 없습니다.

 

여러분 포인터의 크기는 얼마일까요? 모든 분들이 아실거라 믿습니다. 4byte입니다. 포인터란 무엇일까요? 주소를 저장하는 변수입니다. 

 

void 포이터란? 주소를 저장하는 포인터입니다. 음? 이게 다야? 네~ void 포인터는 자료형이 결정되지 않은(어떤 자료형의 주소든) 주소를 저장하는 포인터입니다. (malloc() 함수의 리턴 자료형이 void* 입니다. 함 생각해 보세요.)

 

void 포인터는 어떤 자료형의 주소든 모두모두 담을 수 있습니다.

void main( )
{
    void *pv;
    char c = 'A';
    int n = 10;
    double d = 3.14;

    pv = &c;
    pv = &n;
    pv = &d;
}

 이처럼 어떤 자료형의 주소든 저장할 수 있습니다. 어떤 자료형의 주소든 4byte를 넘지 않을 것이고 void* 형 pv는 단지 주소를 저장하는 포인터입니다. 그렇다면, 주소 pv로 할 수 있는 일은? 자료형이 없어서 아직 할 수 있는 일은 없습니다. 주소는 단지 주소일 뿐 어떤 데이터가 메모리공간에 저장돼 있는지 알 수 없으므로 의미가 없습니다.

 

왜? void 포인터가 의미 없는 주소인지 포인터를 복습해 보도록 할까요?

그림을 보며 설명드리도록 하겠습니다. (이해가 쉽지 않다면 포인터 장을 참고하세요)

void main( )
{
    int n = 10;
    int * pn = &n;

}

 

 

pn이 int* 이므로 *pn은 int형이 됩니다.

 

또, .....

void main( )
{
    int n = 10;
    int * pn = &n;

    int ** ppn = &pn;

}

 

 

 ppn이 int ** 형이므로 *pn은 int* 형이며 **ppn은 int형 정수가 됩니다.

 

이제 다시 void*를 볼까요?

void main( )
{
    void *pv;
    int n = 10;
    pv = &n;

*pv // 에러~~

}

 

 

 

*pv는 연산 에러(컴파일 에러)입니다. void*형에 *연산자를 사용할 수 없습니다.

 

가지고 있는 주소 메모리에 접근하려면 아래와 같이 주소 형변환 후 접근할 수 있습니다.

void main( )
{
    void *vp;
    char c = 'A';
    int n = 10;
    double d = 3.14;

    vp = &c;
    printf("%c\n", *(char*)vp );
    vp = &n;
    printf("%d\n", *(int*)vp );
    vp = &d;
    printf("%lf\n", *(double*)vp );
}
  1. A
    10
    3.140000

 vp를 각 자료형으로 변환한 후 *연산자로 메모리 접근합니다.

아래 그림을  참고하세요

 

 

보통 아래 예제처럼 사용합니다.

void main( )
{
    void *vp;
    int *pn;
    int n = 10;
   
    vp = &n;
    pn = (int*)vp;
    printf("%d\n", *pn );
}
  1. 10

pn변수에 대입 후 *pn으로 메모리 접근합니다. 위 예제는 바보 같습니다. 바로 pn을 사용하면 되는 데 복잡하게 사용하고 있으니까요. 하지만, 이것은 예제일 뿐이고 실전에서는 정당한 이 예제와 같이 사용해야 하는 정당한 이유가 있습니다. 지금은 패~스!

 

2, 함수 포인터

포인터는 주소를 저장하는 변수이므로 함수 포인터는 함수의 주소를 저장하는 포인터입니다.

어?? 어어?? 함수의 주소를 저장해? 함수도 주소가 있다는 말인가?

그렇습니다. 실제 함수를 호출하면 함수의 주소를 이용하여 호출합니다.

 

함수의 주소를 출력해 보도록 하겠습니다.

void func( )
{
}
void main ( ) {
    printf("main : %x\n", main);
    printf("func : %x\n", func);
    printf("printf : %x\n", printf);
}
  1. main : 411140
    func : 4111d1
    printf : 1025abb0

함수의 이름은 그 함수의 시작주소입니다. 배열의 이름도 그 배열이 시작하는 시작주소죠? 같습니다.

함수 포인터의 크기는 4byte입니다. 모든 포인터는 4byte입니다. 함수 포인터도 단지 주소를 저장하는 4byte 메모리 공간일 뿐입니다.

아래 그림을 참고하세요.

 

 

 

어떤 함수의 주소가 K라면 우리는 함수의 주소를 이용하여

  •  K( );
  •  함수주소( );

이렇게 함수를 호출하는 것입니다.

 

함수 포인터란 위와 같은 함수의 주소를 저장하는 포인터입니다.

int형 주소는 int형 포인터에

double형 주소는 double형 포인터에

함수형 주소는 함수형 포인터에 저장합니다.

 

함수 포인터 선언

함수의 원형만 알면 쉽게 포인터를 선언할 수 있습니다.

예로

void func( )
{
}

원형은 ?

void func( ); 입니다.

위 함수의 주소를 저장하는 포인터 변수 선언은 void (*pf)( );

 

void  func(int n )
{
}

원형은 ?

void func(int  ); 입니다. 

위 함수의 주소를 저장하는 포인터 변수 선언은 void (*pf)(int );

원형과 같고 변수에 (* ) 연산자를 붙이면 됩니다. 연산자가 없으면 함수 원형 선어이 되겠죠?

 

간단한  함수의 주소를 저장하는 예제입니다.

void func( )
{
    printf("함수 포인터 예제!\n");
}
void main ( )
{
    void (*pf)( );
    pf = func ;

    func( );
    pf( );
}
  1. 함수 포인터 예제!
    함수 포인터 예제!

 함수포인터 pf를 선언하고 함수의 주소(func)를 저장한 후 함수를 호출하고(pf() ) 있습니다.

 

아래와 같이 호출할 수도 있습니다.

void func( )
{
    printf("함수 포인터 예제!\n");
}
void main ( )
{
    void (*pf)( );
    pf = func ;

    func( );
    (*pf)( );
}
  1. 함수 포인터 예제!
    함수 포인터 예제!

 함수 포인터는 pf()와 (*pf)() 로 포인터가 가리키는 함수를 호출할 수 있습니다. pf는 함수의 주소인데 어떻게 (*pf)와 같나? 이렇게 의문이 생길 수 있지만 함수 포인터는 완벽히 동일합니다. pf는 함수의 주소이며 *pf도 함수의 주소입니다. 연산자 공부할 때 연산자는 위치와 사용 용도에 따라 연산자 의미가 달라진다고 했습니다.

pf와 *pf는 함수의 주소로 같다. 아래 예제를 보시죠!

void func( )
{
    printf("함수 포인터 예제!\n");
}
void main ( )
{
    void (*pf)( );
    pf = func ;

    printf("%x  %x  %x\n", func, pf, *pf);
}
  1. 4111d1   4111d1   4111d1

모두 같은 함수의 주소입니다.

 

하나의 함수 포인터로 서로다른 함수를 호출하는 예제입니다.

void func1(int n1, int n2)
{
    printf("함수 func1(  ) 인자 : %d, %d\n", n1, n2);
}
void func2(int n1, int n2)
{
    printf("함수 func2(  ) 인자 : %d, %d\n", n1, n2);
}
void func3(int n1, int n2)
{
    printf("함수 func3(  ) 인자 : %d, %d\n", n1, n2);
}
void main ( )
{
    void (*pf)(intint );
    pf = func1 ;
    pf( 10, 20);

    pf = func2 ;
    pf( 10, 20);

    pf = func3 ;
    pf( 10, 20);
}
  1. 함수 func1(  ) 인자 : 10, 20
    함수 func2(  ) 인자 : 10, 20
    함수 func3(  ) 인자 : 10, 20

 간단하죠? ㅡㅡ; 패~스!

 

마지막으로 함수 포인터를 사용하는 이유는 무엇일까요?

첫째, 서로 다른 함수에 대해 동일한 인터페이스를 제공한다. 같은 이름으로 서로다른 함수를 호출할 수 있다는 것입니다.

둘째, 실행시간에 알고리즘이나 기능을 변경하는 유연성을 제공한다.

셋째, 콜백(callback) 메커니즘을 제공한다. 등..


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

728x90
728x90

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

 

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

 

 

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

 

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

 

 

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

 

map, multimap의 주요 특징과 함수

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

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

 

기본적인 map 예제


#include <iostream>
#include <map>
using namespace std;
void main( )
{
    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의 트리 그림입니다.

 

 

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

 

 

 

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

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


#include <iostream>
#include <map>
using namespace std;
void main( )
{
    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,intpr( 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에 저장합니다.

그림으로 표현하면

 

 

 

 

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


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

void main( )
{
    map<int , int > m;

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

    map<intint>::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의 검색 함수들입니다.


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

void main( )
{
    map<int , int > m;

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

    map<intint >::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<intint>::iterator, map<intint>::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에서 설명했으므로 패스~!

그림으로 설명하면...

 

 

 

 

 

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

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

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

void main( )
{
    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<intint>::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과 같습니다.

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

 



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

728x90

'Basic Programming > STL' 카테고리의 다른 글

STL - Set, MultiSet 컨테이너  (0) 2016.04.04
STL - Deque 컨테이너  (0) 2016.04.04
STL - String 컨테이너  (0) 2016.04.04
STL - List 컨테이너  (0) 2016.03.23
STL - Vector 컨테이너  (0) 2016.03.23
728x90

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


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

 

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

 


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

 

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

 

 

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

 

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

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

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

 

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

#include <iostream>
#include <set>
using namespace std;
void main( )
{
    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;
void main( )
{
    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;

void main( )
{
    set<int , greater<int> > s;

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

    set<intgreater<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;

void main( )
{
    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;

void main( )
{
    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는 두 번째 값을 저장하는 변수입니다.

그림으로 간단하게...

 

 

 

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

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

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

 

equal_range()의 예제입니다.

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

void main( )
{
    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에 해당합니다.

 

 그림으로 보면...

 

 

 

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

 

 

 

lower_bound와 upper_bound 예제입니다.

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

void main( )
{
    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()은 찾은 데이터 위치의 다음 요소를 가리키는 반복자입니다.

 그림으로 보면...

 

 

 

 

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

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

 

간단한 multiset의 출력입니다.

#include <iostream>
#include <set>
using namespace std;
void main( )
{
    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;

void main( )
{
    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() 함수를 사용하면 찾은 데이터의 구간 반복자 쌍을 얻을 수 있습니다.

설명은 그림으로...

 

 

 

 

2, typedef 내장 자료형

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

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

 

key_comp()함수 예제

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

void main( )
{
    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;

void main( )
{
    multiset<intgreater<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 입니다.


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

728x90

'Basic Programming > STL' 카테고리의 다른 글

STL - Map, MultiMap 컨테이너  (0) 2016.04.04
STL - Deque 컨테이너  (0) 2016.04.04
STL - String 컨테이너  (0) 2016.04.04
STL - List 컨테이너  (0) 2016.03.23
STL - Vector 컨테이너  (0) 2016.03.23
728x90

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

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

 

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

 

 

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

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

 

1. deque의 특징과 주요 함수

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

 

데이터 추가와 출력

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

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

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

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

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

 

 

 

 

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

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

void main( )
{
    deque<int> dq;

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

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

    dq.push_front(100);
    dq.push_front(200);
    dq.push_front(300);
    forint 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()로 앞쪽에 데이터 요소를 추가하면 새로운 메모리 단위를 생성하고 데이터를 추가합니다.

위 예제의 그림입니다.

 

 

 

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

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

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

void main( )
{
    deque<int> dq;

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

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

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

    iter = dq.insert( iter , 500);


    forint 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도 비슷하게 동작합니다.

위 예제의 그림입니다.

 

 

 

 

 

2, deque의 반복자

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

 

반복자 예제

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

void main( )
{
    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처럼 '임의 접근 반복자'를 제공합니다.


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

728x90

'Basic Programming > STL' 카테고리의 다른 글

STL - Map, MultiMap 컨테이너  (0) 2016.04.04
STL - Set, MultiSet 컨테이너  (0) 2016.04.04
STL - String 컨테이너  (0) 2016.04.04
STL - List 컨테이너  (0) 2016.03.23
STL - Vector 컨테이너  (0) 2016.03.23
728x90

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의 주요 개념을 그림으로 표현하면

 

 

 

 

1, string의 특징과 주요 함수

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

 

간단한 문자열 출력 예제.

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

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

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

 

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

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

void main( )
{  
    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;
void main( )
{  
    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;

void main( )
{  
    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;

void main( )
{  
    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;

void main( )
{  
    string str;

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

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

 

 

기본적인 문자열 함수.

 #include <iostream>
#include <string>
using namespace std;
void main( )
{  
    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;

void main( )
{  
    string str = "Hello!";

    const char * 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은 '임의 접근 반복자'를 지원하므로 임의 접근 반복자를 사용하는 여러 알고리즘을 이용할 수 있습니다.


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

728x90

'Basic Programming > STL' 카테고리의 다른 글

STL - Set, MultiSet 컨테이너  (0) 2016.04.04
STL - Deque 컨테이너  (0) 2016.04.04
STL - List 컨테이너  (0) 2016.03.23
STL - Vector 컨테이너  (0) 2016.03.23
STL - Standard Template Library (STL)  (0) 2016.03.22

+ Recent posts