본문 바로가기

[ C/ C++ 프로그래밍 ]/[ MFC ]

[ MFC ] C++의 주제들 - 1

ㅇ 참고 및 출처  :


○ 비트 플래그

특정한 상태의 기록을 나타내기 위해서 사용된 변수를 깃발(Flag)변수라고 한다.

어떤 부호 없는 정수타입 변수(unsigned int)의 이진 비트열 각각의 비트들이 이러한 깃발 변수로 사용되었을 때, 이를
비트 플래그(bit flag)라고 한다.

예를 들어 윈도우win32 시스템에서 정수 타입 변수는 32비트 이므로 32가지의 On/Off 상태를 기록할 수 있다.
만약 상태의 개수가 각각 4가지라면, 상태를 나타내는데 2비트가 필요하므로 16가지의 깃발 변수로 사용할 수 있다.
각 비트의 상태를 변경하기 위해 비트 마스크(bit mask)를 사용한다.

비트 마스크(bit mask)는 비트 연산자 &,|를 사용하여 특정한 비트를 1(bit set)로 혹은 0(bit clear)으로
만드는것을 말한다.


 &와 | 연산은 위와 같은 특징을 가진다.

예를 들면 MessageBox() 함수가 버튼의 종류를 나타내기 위해, 최하위 4비트( 3, 2, 1, 0 위치 )를 사용하고 , ( 나타낼 수 있는 버튼의 종류
는 16가지가 된다) 아이콘의 종류를 나타내기 위해 그 다음 4비트 ( 7, 6, 5, 4 위치) 를 사용하다고 가정하면 버튼의 종류와 아이콘의 종류
를 다음과 같이 나타낼 수 있다.
(예제를 보면 버튼의 종류가 6가지이므로, 3비트면 충분하지만 확장성을 위해 4비트를 예약한것 같다)




 버튼의 종류와 아이콘의 종류를 나타내는 비트 플래그를 설정하기 위해 비트OR연산자를 이용해서
아래 처럼 설정할 수 있다.
 
  MB_OK | MB_ICONQUESTION

또한 설정된 비트 플래그 flag에서 아이콘의 상태를 얻어 내기 위해 아래 처럼 사용할 수 있다.

   iIconState = ( falg & 0x00f0 ) >> 4;




소스 다운






앞으로 비트 플래그를 파라미터로 전달받는 많은 함수들을 보게 될 것이다.


○ 함수 포인터
 함수 포인터란 함수의 시작 주소를 가리키는 포인터를 말한다. Win32 환경에서 포인터는 4바이트며, 함수 포인터의 4바이트 내용은
특정 함수의 시작 주소를 담고 있다.

함수 포인터를 이용하면 일반적인 함수(generic function)를 설계 할 수 있다.
 뒤에 가서 함수포인터를 일반적인 디자인(generic design)에 적용하는 예를 5장과 6장에서 살펴봄



소스 다운 :
 


실행파일이 메모리에 로드(load)되면 함수나 변수들이 고유한 메모리 주소를 가지게 된다.
이렇게 함수나 변수의 실제 주소를 결정하는 것은 바인딩(binding)이라고 한다.




 위의 그림 대로 바인되었다면 아래의 표현식(expression) 은 Sum()함수의 시작 주소인 [500]을 의미한다.
   sum;
문장(Statement)이 정수, 혹은 실수 처럼 어떤 값(vlaue)을 가질 때 이러한 문장(statement)을 표현식이라고 한다.
문장은 ;으로 끝나므로 쉽게 구분이 가능하다. 문장에서 ;을 제외한 부분이 표현식이 되려면, 그것이 값이어야 한다.
예를 들면 2 + 3; 라는 문장에서 2+3은 5라는 값을 가지므로 표현식이다. 하지만 return; 이라는 문장에서 return은 값을 가지지
못하므로 표현식이 아니다. 표현식은 등호(=)의 오른쪽에 사용될 수 있음을 의미한다. 함수의 이름이 표현식임을 아는 것은 중요하다.
왜냐하면 그것은 함수의 이름이 등호의 오른쪽에 사용될 수 있음을 아는 것이기 때문이다.
 
  sum( 2,3);   ==> 500버지의 함수를 호출하고 파라미터로 2와 3을 전달하는 것

 함수의 시작 주소를 다른 변수에 저장할 필요가 있을 떄, 함수 포인터 변수(pointer to a function)를 선언한다. 이것이 변수(함수가 아닌)임에
주의해야 한다. 함수 포인터를 선언할 때는 대입하고자 하는 함수의 원형(prototype)을 고려한 특별한 문법을 따라야 한다.
 
  int (*fp) (int, int );   // 특별히 변수 fp를 둘러싼  괄호를 반드시 명시해야 한다. 괄호가 없다면 이것은 변수 선언이 아니라 함수 선언이다.

 또한 int Sum(int,int)의 원형에 해당하는 부분을 명시해야 한다.

함수 포인터 변수를 선언하기 위해서는 단지 함수 이름 Sum앞에 *를 붙이고 괄호로 묶어주는면 된다.


○ .*, ->* 연산자
 CStateManager를 클래스라고 할 때 아래 문장은 CStateManager의 멤버 함수 중 프로토타입(prototype)이 void CStateManager::Name(int);
인 멤버 함수의 시작 주소를 가지는 멤버 함수에 대한 포인터 변수(pointer to a memver function)의 선언이다.

  void (CStateManager::*fp) (int );

아래 와 같은 CStatemManager의 멤버 함수 SetState()를 고려해보면

  void CStateManager::SetState( int state )
  {
          m_iState = state;
   }

이제, 아래의 문장은 SetState()의 시작 주소를 fp에 대입한다.
   void  ( CStateManager::*fp) (int) ;

   fp = &CStateManager::SetState;

이렇게 멤버 함수에 대한 포인터 변수가 선언되었을 떄, 특정한 객체와 연관지어 fp를 사용하고자 할떄, .*(dot, asteriak)
혹은 ->*(minus, greater than, asterisk ) 연산자를 사용한다.











소스 다운





아래의 두 문장은 같은 의미이다.
 (sam.*fp ) (1) ;  == sman.SetState(1);

멤버 함수 포인터 fp가 sman의 멤버가 아니라는 사실에 주목하자. 그래서 Sman.fp 혹은 sman->fp 같은 사용은 불법이다.
만약 sman이 CStateManger의 포인터 타입으로 선언된다면 (sman)->*gp(1) 처럼 사용해야 한다.

각각의 상태에 대해 DoIt()에서 호출해야 하는 함수는 3가지다.  그래서 DoIt()은 다음과 같이 구현할수있다.
단, m_nState가 0보다 크고 2보다는 작은 값을 가진다는 조건을 가져야 한다.




DoIt() 내부에서 상태의 수에 상관없이 각 상태에 필요한 함수를 상태의 비교 없이 즉시 호출함에 주목하자.
또한 각 상태가 독립적인 함수로 작성됨으로써, 읽기 좋고, 유지/보수가 쉬운코드가 된다는 것도 기억하자

*여난자와 ->* 연산자의 용도를 이해하는 것은 매우 중요하다  나중에 윈도우 프로시저를 위와 같은 기법으로
자동화할 것인데, MFC에서는 것을 메시지 맵(message map)이라고 부른다.