[출처 : http://www.jamsun2.com/zbxe/?mid=study&document_srl=94602&page=6&sort_index=readed_count&order_type=desc]


char* strtok(char* src, const char* delim)
{
  // src, delim 이 NULL 인지, delim 이 "" 인지 체크하는 코드는 생략
  char* tok;
  static char* next;     // 분석을 시작할 위치
  if (src != NULL)
    next = src;
  tok = next;

  // boundary condition check
  if (*next == '\0')
    return NULL;

  // 분석 시작
  for (; *next != '\0'; ++next)
  {
    if (*next in delim) // pseudo code
    {
      *next = '\0';
      ++next;
      break;
    }
  }

  return tok;
}



char* strtok(char* src, const char* delim, char** start)
{
  // src, delim 이 NULL 인지, delim 이 "" 인지 체크하는 코드는 생략
  char* tok;
  char* next;     // 분석을 시작할 위치. static 을 없애고 start 라는 입력
                  // 으로 초기화함
  if (src != NULL)
    next = src;
  else
    next = *start;


  // boundary condition check
  if (*next == '\0')
    return NULL;

  // 분석 시작
  tok = next;
  for (; *next != '\0'; ++next)
  {
    if (*next in delim) // pseudo code
    {
      *next = '\0';
      ++next;
      break;
    }
  }

  *start = next;
  return tok;
}

[출처 : http://www.joinc.co.kr/modules/moniwiki/wiki.php/Code/C/token]


사용방법

char *token(char *str, char *src, const char* sep); 
 
  1. str : 토큰 분리를 위한 원본 문자열 
  2. src : 토큰 분리후 다음 문자열을 가르키는 포인터 
  3. sep : 토큰 
  4. return : 더이상 토큰이 없거나 '\0'을 만나면 

#include "token.h" 
 
int main() { 
   char src[20]; 
   char str[20]; 
   char* next = &src[0]; 
   strcpy(src, "ab1de12eoqd12dq"); 
   while(next = token(str, next, "12")) { 
      printf("%s\n", str); 
   } 
   return(0); 
} 
 

코드

#include <stdio.h> 
 
char* token(char* str, char* src, const char* sep) { 
   int i = 0; 
   if (*src == '\0') return(NULL); 
   while (1) { 
      if (sep[i] == '\0') { 
         str -= strlen(sep); 
         break; 
      } else if (*src == sep[i]) { 
         i++; 
      } else i = 0; 
      if (*src == '\0') break; 
      *str++ = *src++; 
   } 
   *str = '\0'; 
   return(src); 
} 
[출처 : http://mwultong.blogspot.com/2007/06/c-1-get-bit-read-bit.html]


부호없는 16비트 정수 즉 "unsigned short"형의 숫자에서, 특정 비트가 1인지 0인지를 알아내는 방법입니다. 다음의 getAbit(숫자, 몇번째비트) 함수를 사용하면 0또는 1을 반환합니다. C에는 2진수 출력 기능이 없기에, ushortToBinary() 라는 함수도 만들었습니다.

n번째 비트, 1인지 0인지 여부 판단 함수 예제


소스 파일명: example.cpp
#include <stdio.h>

int getAbit(unsigned short x, int n);
char *ushortToBinary(unsigned short i);


int main(void) {

  unsigned short num = 24761;
  printf("%s\n", ushortToBinary(num)); // 2진수로 출력
  // 출력 결과: 0110000010111001


  // 0번 비트 (맨 우측 비트) 읽기
  printf("%d\n", getAbit(num, 0));
  // 1


  // 1번 비트 (맨 우측에서 2번째 비트) 읽기
  printf("%d\n", getAbit(num, 1));
  // 0



  // num을 1비트씩 읽어, 2진수로 출력하기
  printf("\n[ ");
  for (int i = 15; i >= 0; i--) {
    printf("%d", getAbit(num, i));
  }
  printf(" ]\n");
  // [ 0110000010111001 ]


  return 0;
}




// 지정한 정수에서, 몇번째 비트만 읽어서 반환하는 함수
int getAbit(unsigned short x, int n) { // getbit()
  return (x & (1 << n)) >> n;
}


// 16비트 정수를 2진수 문자열로 변환 함수
char *ushortToBinary(unsigned short i) {
  static char s[16 + 1] = { '0', };
  int count = 16;

  do { s[--count] = '0' + (char) (i & 1);
       i = i >> 1;
  } while (count);

  return s;
}

[출처 : http://mwultong.blogspot.com/2007/06/c-bit-on-off-set-bit-save-bit.html]


부호없는 16비트 정수 즉 "unsigned short"형 숫자에서 특정 비트를 켜고 끄는 방법입니다. 아래의 "setAbit(숫자, 몇번째비트, 1또는0)" 함수를 사용하면 됩니다. 특정 비트를 1또는 0으로 설정한 후, 그 결과를 다시 "unsigned short"으로 반환합니다. 따라서 이 함수로, 어떤 숫자를 비트 단위로 조작할 수 있습니다.

특정 비트 조작, 1 또는 0으로


소스 파일명: example.cpp
#include <stdio.h>

unsigned short setAbit(unsigned short x, int n, int b);
char *ushortToBinary(unsigned short i);


int main(void) {

  unsigned short num = 0;
  printf("%s\n", ushortToBinary(num));
  // 2진수 출력 결과: 0000000000000000


  // 0번 비트 (맨 우측 비트)를 1로 켜기
  printf("%s\n", ushortToBinary( setAbit(num, 0, 1) ));
  // 0000000000000001



  printf("\n\n");
  // 0000000000000000 에서, 차례로 1비트씩 켜기 (1로 ON)
  num = 0;
  for (int i = 15; i >= 0; i--) {
    unsigned short temp = setAbit(num, i, 1);
    printf("%s = %10u\n", ushortToBinary(temp), temp);
  }


  printf("\n\n");
  // 1111111111111111 에서, 차례로 1비트씩 끄기 (0으로 OFF)
  num = 0xFFFF;
  for (int i = 15; i >= 0; i--) {
    unsigned short temp = setAbit(num, i, 0);
    printf("%s = %10u\n", ushortToBinary(temp), temp);
  }


  return 0;
}




// 정수 x 의 n번째 비트를, b로 설정하는 함수
unsigned short setAbit(unsigned short x, int n, int b) { // setbit()

  if (b == 1)
    return (unsigned short) (
                             x | (1 << n)
                             );

  return (unsigned short) (
                           x & (~(1 << n))
                           );

}


// 16비트 정수를 2진수 문자열로 변환 함수
char *ushortToBinary(unsigned short i) {
  static char s[16 + 1] = { '0', };
  int count = 16;

  do { s[--count] = '0' + (char) (i & 1);
       i = i >> 1;
  } while (count);

  return s;
}



컴파일 및 실행 결과 화면:
D:\Z>cl example.cpp && example.exe
example.cpp
0000000000000000
0000000000000001


1000000000000000 =      32768
0100000000000000 =      16384
0010000000000000 =       8192
0001000000000000 =       4096
0000100000000000 =       2048
0000010000000000 =       1024
0000001000000000 =        512
0000000100000000 =        256
0000000010000000 =        128
0000000001000000 =         64
0000000000100000 =         32
0000000000010000 =         16
0000000000001000 =          8
0000000000000100 =          4
0000000000000010 =          2
0000000000000001 =          1


0111111111111111 =      32767
1011111111111111 =      49151
1101111111111111 =      57343
1110111111111111 =      61439
1111011111111111 =      63487
1111101111111111 =      64511
1111110111111111 =      65023
1111111011111111 =      65279
1111111101111111 =      65407
1111111110111111 =      65471
1111111111011111 =      65503
1111111111101111 =      65519
1111111111110111 =      65527
1111111111111011 =      65531
1111111111111101 =      65533
1111111111111110 =      65534
D:\Z>

[출처 : http://kldp.org/node/63623]

코딩은 해당업체에 룰을 따랐습니다. 헤더만을 올려놓습니다. 소스는 쫌...엉망입니다...앞서 이야기 했던 네트워크처리를 위한 부분입니다.

#ifdef __cplusplus
extern "C" {
#endif

/******************************************************************************
 DISPATCHER			: Dispatcher 식별자
 size_t size		: 현재 등록되어 있는 fd 수
 size_t maxfds		: 최대 등록 가능한 fd 수(필요에 따라 수시로 증가)
 pollFDs			: 현재 사용하고 있는 struct pollfd 구조체 포인터
******************************************************************************/
typedef struct
{
	size_t size;
	size_t maxfds;
	struct pollfd *pollFDs;
} DISPATCHER;

/******************************************************************************
 Dispatcher_Create(const size_t maxfds)
 Dispatcher_Create는 calloc를 통해 DISATCHER구조체를 생성하고 생성된 구조체를
 반환한다.
******************************************************************************/
DISPATCHER *Dispatcher_Create();


/******************************************************************************
 Dispatcher_Destroy(DISPATCHER *dispatch)
 Dispatcher_Create를 통해 생성된 구조체를 free를 통해 소멸시킨다.
******************************************************************************/
void Dispatcher_Destroy(DISPATCHER *dispatch);


/******************************************************************************
 Dispatcher_Add(DISPATCHER *dispatch)
 DISPATCHER의 식별자에 fd를 추가함으로써 poll이 fd를 감시할 수 있도록 한다.
******************************************************************************/
int Dispatcher_Add(DISPATCHER *dispatch, const int fd);


/******************************************************************************
 Dispatcher_Del(DISPATCHER *dispatch)
 DISPATCHER의 식별자에 fd를 삭제함으로써 poll이 fd를 감시에서 제거한다.
******************************************************************************/
int Dispatcher_Del(DISPATCHER *dispatch, const int fd);


/******************************************************************************
 Dispatcher_Wait(DISPATCHER *dispatch, const long timeout)
 등록된 fd들을 감시한다. 또 한 이 함수를 사용한 시점에서는 Block되며 이벤트
 가 발생할때까지 대기하게 된다. timeout으로 대기시간을 설정하게 되면 대기시
 간 이후에 함수를 빠져나가게 된다. 대기시간은 밀리세컨드이다.
******************************************************************************/
int Dispatcher_Wait(DISPATCHER *dispatch, const long timeout);


/******************************************************************************
 Dispatcher_CurfdsSize(DISPATCHER *dispatch) 
 DISPATCHER 에 현재까지 추가된 소켓의 수를 반환한다.
******************************************************************************/
size_t Dispatcher_CurfdsSize(DISPATCHER *dispatch);

#ifdef __cplusplus
}

poll을 객체형식으로 만들어 놓은겁니다. poll의 지저분한(?) 고유 코드는 다 빠지고 이것만으로 하니 별 신경쓸께 없었습니다. 위의 함수군을 사용하는 일은 다른 사람은 없었습니다. 이것은 밑의 Reactor을 만들기 위해 만들어 놓은 것이죠. 써도 별상관 없겠지만 이걸 익히려 노력은 안하리라 생각했기 때문입니다.

다음은 Reactor패턴을 C수준에서 응용한겁니다. 더 세밀히 하자면 함수포인터를 사용해야 했으나..그럴만한 시간적..ㅜㅜ..

Client Handler(CHandler)는 접속된 각각의 개별 커넥션을 말합니다. 그리고 이것들의 이벤트를 관리하며 처리하는 Reactor이 존재합니다. 사용자는 CHandler의 이벤트 발생시에 처리할 부분을 작성하게 됩니다. 즉 비지니스 로직이 들어갑니다.

Reactor은 TCPCLIENT 객체를 추가하게 되면 CHANDLER(Client Handler)를 생성하게 됩니다. 그리고 CHandler_*함수를 각 이벤트 분류에 따라 호출하게 해줍니다. '유저가 작성해야할 함수' 가 가상함수부분인데...여하튼 이 부분을 사용자가 작성해야 전체가 완성됩니다. 이 부분은 비지니스 로직이 들어가야할 부분이죠. 이것을 바탕으로 OnPacket(..)함수등을 만들어 패킷 처리를 하고 그랬었습니다.

CHandler 클래스를 상속받고, 가상함수등을 이용하고 Reactor에 등록시에도 그렇게 하여야 겠으나 그냥 어찌어찌 만들어진 코드입니다. 핑계를 데면 급히 만든 코드이니......... 또 관리자가 STL을 모른다른 이유로 또 C라는 한정적인 이유로 그냥 배열로 CHandler들을 관리하게 될 허접 SOCKETBOX를 만들었습니다.

그리 잘 따른 코드는 아니나 프레임웍이라 생각하고 보신다면 비지니스로직과 네트워크단의 분류가 확실히 되었음을 아실겁니다.

비지니스로직단에서는 마치 쓰레드를 사용하는것 마냥 착각하게 됩니다.

#ifdef __cplusplus
extern "C" {
#endif

typedef struct
{
	SOCKETBOX	*socketBox;
	DISPATCHER	*dispatcher;
	void		*mainArg;
} REACTOR;

typedef struct
{
	REACTOR		*reactor;
	TCPCLIENT	*tcpClient;
	TIMESPEC	 timeout;
	TIMESPEC	 lastAccessTime;
} CHANDLER;


//유저가 작성해야 하는 함수==================================================
//연결되었을때 호출
int  CHandler_Open(CHANDLER *cHandler, TCPCLIENT *tcpClient, void *mainArg);
//데이터가 들어왔을때 호출
int  CHandler_Input(CHANDLER *cHandler, TCPCLIENT *tcpClient);
//연결이 끊겼을때 호출
void CHandler_Close(CHANDLER *cHandler, TCPCLIENT *tcpClient);
//예약한 시간설정이 되었을때 호출
int  CHandler_TimeOut(CHANDLER *cHandler, TCPCLIENT *tcpClient);

//  REACTOR 생성시 호출될 함수
void  Reactor_Open(REACTOR *Reactor);
//  REACTOR 타임아웃시 호출될 함수
void  Reactor_TimeOut(REACTOR *Reactor);
//  REACTOR 파괴시 또는 종료시 호출될 함수
void  Reactor_Close(REACTOR *Reactor);
//===========================================================================


//  REACTOR 생성자
REACTOR  *Reactor_Create(REACTOR *mainArg);
//  REACTOR 파괴자
void  Reactor_Destroy(REACTOR *Reactor);
//  REACTOR 실행
void  Reactor_Run(REACTOR *Reactor);

//유저가 사용할 수 있는 함수=================================================
//새로운 tcpClient를 등록
int   Reactor_Add(REACTOR *Reactor, TCPCLIENT *tcpClient);
//tcpClient를 삭제
int   Reactor_Del(REACTOR *Reactor, TCPCLIENT *tcpClient);

//타임아웃 설정
void  CHandler_SetTimeOut(CHANDLER *cHandler const int sec);

//fd값을 돌려줌
int   CHandler_Getfd(CHANDLER *cHandler);

//최근 접근 시간 셋팅함.
int   CHandler_SetAccessTime(CHANDLER *cHandler);

//최근 접근 시간 셋팅후 지난 시간을 밀리세컨드로 반환
long  CHandler_GetPastTime(CHANDLER *cHandler);

//자신의 아규먼트를 저장
int   CHandler_SetUserArg(CHANDLER *cHandler void *userArg);

//해당 핸들러에 저장한 아규먼트를 가져옮
void *CHandler_GetUserArg(CHANDLER *cHandler);

//생성시 저장했던 아규먼트를 가져옮
int   CHandler_GetMainArg(CHandler *cHandler);

//TCPCLIENT객체를 가져옮
TCPCLIENT *CHandler_GetTcpClient(CHandler *cHandler);

#ifdef __cplusplus
}
#endif

편집과정에서 몇가지 빠진함수가 있기도 하네요.

위와같이 작성하고 CHandler에서 사용할 변수도 CHandler_SetUserArg로 구조체를 통해 등록하여 사용했습니다. 따라서 전역변수를 일체 사용하지 않아도 되는 코드를 생성할 수 있었으며 연결당 개별관리를 Reactor가 CHandler이란것을 통해 해주었으며 CHandler_Open등에서 -1을 리턴하면 무조건 CHandler_Close를 호출하도록 하여 Close함수를 쓸 이유도 없게 되었구요.

특히나 편리했던 부분은 TimeOut부분을 Reactor에서 관리하고 이벤트처리하여 주므로 프로토콜 문서에 따라 복잡해질 로직이 아주 심하게 편해졌다고 말씀 드릴 수 있네요..TimeOut은 커넥트당 개별관리를 Reactor에서 해줍니다. 일반적으로 처리하는 방식인 일정 시간마다 전체 루프를 돌며 하는것이 아니기 때문에 좀 더 안정적이라고 할 수 있겠죠.

이것을 사용한 결과...다른사람이 사용함으로써 생기는 문제에 있어서 제가 그 사람의 비지니스 로직까지 알 필요 없이 이 부분에 대해서만 점검하거나 버그 수정정도에서 마무리 됩니다. 전 이에 관련된 문서나 알고리즘(?) 정도만 철저히 하면 제가 맡은 소켓 라이브러리 부분은 완료 되는 것이었죠.

다른 사람들은 커넥트되는것을 또 데이터가 들어오는것을, 타임아웃되는것을 직접 코드로 작성할 이유가 없었습니다. 그냥 이벤트가 들어오는 데로 처리하면 되는거니까요. 최소한의 지식은 커넥트와 센드, 서버로 띄우는것인데요. 이것들도 랩핑하여 라이브러리화 했습니다.

아마도 C만 하신 분들은 아주 심하게 생소하리라 추측됩니다. 이의가 있으신분은 언제든지요.....

실력자분들이 엄청 많을텐데...허접한 코드....마니 챙피하네요... :(



[출처 : http://kldp.org/node/63623]
class TestClass
{
 public:
    int badMember;
    void test(int x) {...}
};
int main()
{
    TestClass testClass;
    testClass.test(3);
}

...
struct TestStruct
{
    int badMember;
    void (*test)(TestStruct*, int);  
};
void TestStruct_test(TestStruct*, int x) {}...

int main()
{
    TestStruct testStruct;
    testStruct.test = &TestStruct_test;
    testStruct.test(&testStruct, 3);  // or TestStruct_test(&testStruct, 3);
}

encapsulation은 대강 다음과 같이 할 수 있습니다.

/* in header */
struct PrivateInterface;

struct PublicInterface
{
    PrivateInterface* privateDataOrFunction;
    int publicData;
}

/* in code */
struct PrivateInteface
{
    ....
}



C++ 소스

class Person {
 int age;
public:
 void setAge(const int age);
 int getAGe() const;
};

이걸 C로 구현하면 아래와 같습니다.

#include <stdio.h>

struct Person {
 int age;
 void (*setAge)(struct Person * this, const int age);
 int (*getAge)(const struct Person * this);
};

void Person_setAge(struct Person * this, const int age)
{
  this->age = age;
}

int Person_getAge(const struct Person * this)
{
 return this->age;
}

void Person_init(struct Person * this)
{
 this->setAge = Person_setAge;
 this->getAge = Person_getAge;
}

void Person_destroy(struct Person * this)
{
}

int main()
{
 struct Person minsu;

 /* 생성자 호출 */
 Person_init(&minsu);

 minsu.setAge(&minsu, 10);
 printf("%d\n", minsu.getAge(&minsu));

 /* 소멸자 호출 */
 Person_destroy(&minsu);
 return 0;
}


[출처 : http://kldp.org/node/58392]

/* Original Source Author : Joe Wright (12/06/2006) */

#include <stdio.h>
#include <ctype.h> // isspace()
#include <string.h> // strcpy_s() strlen()

#define MAX_STR_LEN 4000

char*  trim(char *s); // 문자열 좌우 공백 모두 삭제 함수
char* ltrim(char *s); // 문자열 좌측 공백 제거 함수
char* rtrim(char* s); // 문자열 우측 공백 제거 함수


int main(void) {
  char* s = "  AAAA  ";

  printf("[%s]\n",  trim(s)); // 출력결과: [AAAA]
  printf("[%s]\n", ltrim(s)); // 출력결과: [AAAA  ]
  printf("[%s]\n", rtrim(s)); // 출력결과: [  AAAA]

  return 0;
}




// 문자열 우측 공백문자 삭제 함수
char* rtrim(char* s) {
  char t[MAX_STR_LEN];
  char *end;

  // Visual C 2003 이하에서는
  // strcpy(t, s);
  // 이렇게 해야 함
  strcpy_s(t, s); // 이것은 Visual C 2005용
  end = t + strlen(t) - 1;
  while (end != t && isspace(*end))
    end--;
  *(end + 1) = '\0';
  s = t;

  return s;
}


// 문자열 좌측 공백문자 삭제 함수
char* ltrim(char *s) {
  char* begin;
  begin = s;

  while (*begin != '\0') {
    if (isspace(*begin))
      begin++;
    else {
      s = begin;
      break;
    }
  }

  return s;
}


// 문자열 앞뒤 공백 모두 삭제 함수
char* trim(char *s) {
  return rtrim(ltrim(s));
}

map과 multimap 데이터 타입이 제공하는 멤버 함수에 관해 자세히 살펴본다. 멤버 함수들이 기초적인 연산들을 제공하는 반면에, 13절과 14절에 설명하고 있는 generic 알고리듬을 사용함으로써 데이터 구조를 보다 유용하게 사용할 수 있게 된다. 

9.2.1 map의 선언과 초기화

map의 선언은 표준 라이브러리에서 자주 보아왔던 방식을 따른다. map은 템플릿 데이터 구조이며, 템플릿의 인자로 키의 타입, 키에 대응되는 값의 타입, 키를 비교하는데 사용될 비교 연산자를 지정해야 한다. 만약에 컴파일러가 디폴트 템플릿 인자(아직은 많은 회사에서 지원하지 않는 C++의 새특징)를 지원하면, 방금 언급한 템플릿 인자 중 세번째 것은 생략이 가능하고, 생략되는 경우에는 키의 타입에 정의되어 있는 less-than 연산자로 지정된다. map은 초기값없이 선언될 수 있고, 한쌍의 반복자를 사용하여 다른 컨테이너에 담긴 값들로 초기화 될 수도 있다. 후자의 경우에, 반복자는 pair 타입의 값을 가리키고 있어야 하며, 이때 첫번째 필드는 키(key)로 간주되고, 두번째 필드는 값(value)으로 간주되는 것이다. 복사 생성자를 사용하여 다른 map의 복사본을 만들어 낸다.
   // map indexed by doubles containing strings
   map<double, string, less<double> > map_one;
   // map indexed by integers, containing integers   
   map<int, int> map_two(aContainer.begin(), aContainer.end());
   // create a new map, initializing it from map two
   map<int, int> map_three(map_two);   // copy constructor
map을 다른 map에 대입할 수 있으며, swap() 연산을 사용하여 두 map간에 값을 서로 교환할 수 있다. 

9.2.2 타입 정의

map과 multimap은 많은 타입 정의들을 포함하고 있으며, 이들은 주로 선언문에서 많이 사용된다. 예를 들어, string을 정수로 매핑하는 map에 사용되는 반복자는 다음과 같이 선언된다.
    map<string, int>::iterator location;
'iterator' 뿐만 아니라, 다음과 같은 타입들이 정의되어 있다. 
 
key_type map을 인덱싱할 때 사용하는 키의 타입
value_type 맵이 담고 있는 데이터 즉 key/value pair 타입
const_iterator 자신이 가리키는 원소를 변경할 수 없는 상수 반복자
reverse_iterator 역방향으로 이동하는 역반복자
const_reverse_iterator 상수 반복자와 역 반복자의 성질을 모두 가지는 반복자
reference A reference to an underlying value.
const_reference A reference to an underlying value that will not permit the element to be modified.
size_type An unsigned integer type, used to refer to the size of containers.
key_compare A function object that can be used to compare two keys.
value_compare A function object that can be used to compare two elements.
difference_type A signed integer type, used to describe the distances between iterators.
allocator_type An allocator used by the container for all storage management.

9.2.3 삽입과 접근

insert() 연산을 사용하여 map이나 multimap에 값들을 삽입한다. 이때 인자는 반드시 키와 값의 pair이어야 한다. 이때 사용되는 pair는 map에 정의되어 있는 value_type이라는 데이터 타입을 사용하여 만든다.

   map_three.insert(map<int>::value_type(5, 7));
한쌍의 반복자를 사용하여 삽입을 수행할 수도 있는데, 다음과 같이 다른 컨테이너로부터 값을 가져올 수 있다.
   map_two.insert(map_three.begin(), map_three.end());

map(multimap은 제외)에서는 첨자 연산을 통해 값을 접근하거나 삽입할 수 있다. 단순히 키를 첨자로 사용하면 해당 값을 접근할 수 있고, 첨자 연산의 결과에 대입을 함으로써 키에 대응되는 값을 변화시킬 수 있다.

   cout << "Index value 7 is " << map_three[7] << endl;
      // now change the associated value
   map_three[7] = 5;
   cout << "Index value 7 is " << map_three[7] << endl;

9.2.4 삭제

키값을 가지고 map과 multimap으로부터 값들을 삭제할 수 있다. multimap에서는 키와 연관된 모든 원소를 삭제한다. 삭제될 원소를 반복자로 지정할 수도 있다. 예를 들면, find() 연산의 결과로 얻은 반복자를 사용할 수도 있다. 한쌍의 반복자를 사용하여 지정한 range내의 원소들을 모두 지우는 것도 가능하다.

   // erase the 4th element 4
   map_three.erase(4);
   // erase the 5th element 
   mtesttype::iterator five = map_three.find(5);
   map_three.erase(five);
   
   // erase all values between the 7th and 11th elements
   mtesttype::iterator seven = map_three.find(7);
   mtesttype::iterator eleven = map_three.find(11);
   map_three.erase(seven, eleven);
원소타입이 소멸자를 정의하고 있다면, 콜렉션으로부터 키와 값을 제거하기 전에 이 소멸자가 호출될 것이다. 

9.2.5 반복자

 No Iterator Invalidation 

begin() end() 멤버 함수는 map과 multimap의 경우 양방향 반복자를 생성한다. 이렇듯, map과 multimap에서 사용되는 반복자들은 양방향 반복자이므로 대소 비교(<, >, <=, >=)가 허용되지 않는다는 점에 유의하자(2.2절의 표를 참고). map이나 multimap에 사용되는 반복자를 참조하면 키(key)와 값(value)의 pair를 얻게 된다. 따라서, 필드명으로 first를 사용하면 키를, second를 사용하면 값(value)을 얻을 수 있다. 첫번째 필드는 상수라서 변경할 수 없지만, 두번째 필드는 주어진 키(key)와 연결된 값(value)을 변화시키는데 사용된다. 원소들은 키 필드의 순서에 따라 접근된다(?). 

rbegin() rend() 멤버 함수는 역방향으로 원소들을 생성하는 반복자들을 반환한다. 

9.2.6 검색과 카운팅

size() 멤버 함수는 컨테이너가 가지고 있는 원소의 갯수를 리턴한다. empty() 멤버 함수는 컨테이너가 비었을 때, true를 리턴하고, size()를 zero와 비교하는 것보다 대체로 빠르다. 

find() 멤버함수는 키를 인자로 취해서, 키/값 pair를 가리키는 반복자를 리턴한다. multimap의 경우에는, 가장 먼저 일치하는 값을 리턴한다. 두경우 모두, 원하는 값을 찾지 못할 때는, past-the-end 반복자를 리턴한다.

   if (map_one.find(4) != map_one.end())
      cout << "contains a 4th element" << endl;

lower_bound() 멤버 함수는 인자로 주어진 키와 일치하는 첫번째 원소를 리턴하고, upper_bound() 멤버 함수는 인자로 주어진 키와 일치하는 마지막 원소 바로 직후의 원소를 리턴한다. 마지막으로,equal_range() 함수는 lower bound와 upper bound를 담고 있는 반복자의 pair를 리턴한다. 이들 연산을 사용하는 예는 이 절의 마지막에 주어진다. 

count() 멤버 함수는 인자로 주어진 키값과 일치하는 원소의 갯수를 리턴한다. map의 경우에는 결과값이 항상 0 또는 1인 반면에, multimap의 경우에는 음수가 아닌 값이면 결과값이 될 수 있다. 단순히 콜렉션이 주어진 키로 인덱싱되는 원소를 포함하는지의 여부만을 확인하고 싶을 때는 find()의 결과값을 end-of-sequence 반복자와 비교하는 것보다는 count()를 사용하는 것이 쉬울 때가 더 많다.

   if (map_one.count(4))
      cout << "contains a 4th element" << endl;

9.2.7 원소 비교

인자가 필요없는 key_comp() value_comp() 멤버함수는 각각 key 타입 또는 value 타입의 원소를 비교할 때 사용되는 함수 객체를 반환한다. 비교할때 사용되는 값들이 콜렉션이 포함되어 있을 필요는 없으며, 이 함수들은 컨테이너에 아무런 영향을 미치지 않는다.

    if (map_two.key_comp (i, j)) // map_two.key_comp()(i, j) ?
       cout << "element i is less than j" << endl;

9.2.8 기타 map 연산

map과 multimap은 ordered 콜렉션이고, map에 대한 반복자는 pair를 반환하기 때문에, 13절과 14절에서 설명하는 함수들이 사용하기에 무의하거나 곤란한 것들이 많다. 그러나, for_each(), adjacent_find(),accumulate()들 각각은 나름대로 쓸모가 있다. 하지만, 인자로 제공되는 함수들은 키/값 pair를 인자로 취하는 것이라야 한다는 것을 반드시 기억해야 한다. 



[출처 : http://web.lge.cn:8000/system/2232]

+ Recent posts