MFC 요점 강의
객체지향적 프로그래밍에서는 프로그램을 객체를 기준으로 나누고 이를 좀더 작은 객체로 나누는 식으로 프로그램을 세분화하고 이 객체를 표현한 클래스를 이용하여 프로그래밍 한다고 하였죠? 게다가 무엇을 객체로 보느냐는 전적으로 프로그래머 맘대로 라로 하였습니다.
그럼 여러분은 윈도우 프로그램을 객체로 세분화 할 때 도대체 프로그램에 무엇을 객체로 정하실 겁니까?
아~ 벌써부터 의견이 분분하군요.. 아무리 프로그래머 맘대로 라고 하였지만 효율적인 방법은 있다고 하였죠?
MFC는 클래스 라이브러리고 각각의 클래스는 객체를 표현한 거 겠네요? 그럼 MFC는 누가 만들었죠?
MS사에서 만들었죠 그러니 MFC(Microsoft Foundation Class)라고 졌겠죠..
그럼 MS사는 윈도우 프로그램을 세분화 할 때 무엇을 객체로 봤는지를 알면 MFC를 그런대로 이해할 수 있겠네요..그럼 한번 봅시다.
MS사는 프로그램의 객체를 크게 4가지로 구분하였습니다.
먼저 눈에 보이는 것부터 설명하면 윈도우가 있지요, 타이틀바도 있고 시스템메뉴도 있고 메뉴도 있고 툴바도 있고, 상태바, 스크롤바등이 있는 윈도우 정학히 말하면 윈도우 틀을 객체로 볼 수 있겠네요..
그 다음에 보이는 것은 대부분 흰 바탕에 글씨도 쓸 수 있고.. 그래프로 그릴 수 있고..아니면 입력한 내용을 시트형태로 보여주던가 그래프 형태로 보여주던가.. 그냥 글자만 보여주던가 하는 입력 또는 출력의 기능을 다 가질 수 있는 클라이언트(Client)영역인데 이를 객체로 볼 수 있겠네요..
그리고 눈에는 보이지 않지만 실제로 데이터를 처리하고 저장하고 하는 부분이 있을 수 있겠죠? 그럼 이것도 객체로 볼 수 있겠구요..
마지막으로 역시 눈에 보이지 않지만 위의 설명한 세 개의 객체를 묶어서 프로그램을 구동시키는 부분이 있어야 할 것 같네요.. 그러면 이것도 객체로 보면 되겠네요..
물론 이것말고도 세세하게 들어가 보자면 더 있을 수 있겠지만 대충 이 정도가 있다고 생각해도 큰 무리는 없습니다.
위의 네 가지를 객체로 보고 프로그램을 크게 세분화 한 것이 MS사가 프로그램을 객체로 세분화하는 방법입니다. 그러면 MS사가 만든 MFC도 위 네 가지 객체를 중심으로 클래스 계층구조를 가지고 있겠군요..
계층구조.. 생각나시죠? 실제로 MFC를 보면 위의 네 객체의 속성을 가지고 있는 클래스를 중심으로 이에 파생된 수백개의 클래스들이 계층구조로 이루어져 있습니다.
시작 단계라서 그런지 MFC도 별거 아닌 것 같군요.. 위에서 맨 먼저 말한 객체 즉 윈도우틀(객체)를 C라는 클래스로 구현되어져 있고,, 두 번째로 클라이언트영역(객체)는 CView라는 클래스로 구현되어져 있습니다. 그리고 세 번째로 자료를 처리 저장하는 부분(객체)은 CDocument라는 클래스로 구현되어져 있습니다. 마지막으로 이들 세 개의 객체를 묶어주고 프로그램을 구동시키는 부분(객체)은 C라는 클래스로 구현되어져 있습니다. 좀전에 때 이를 사용하면 화면에 뭔가 잔뜩 나온다고 했는데 이때 나오는 것이 바로 이 네 개의 클래스입니다.물론 C라는 클래스도 있지만 현단계에서 뭐 그리 중요한 것은 아닙니다.
만들고자하는 프로그램마다 약간의 차이는 있겠지만 이 네게의 클래스가 없으면 윈도우 프로그램을 만들 수 없다는 얘기겠죠.. 그럼 위의 네 개의 클래스는 모든 프로그램을 만드는 부분에서 공통적으로 쓰이는 객체이니까 이걸 먼저 파악하는게 MFC를 파악하는 순서가 되겠네요.. 그러면 앞에서 설명한 모든 윈도우 프로그램에 기본이 되는 4개의 클래스를 중심으로 설명해 나가도록 하겠습니다.
C, CView, CDocument, C 라고 하는 이 4개의 클래스는 대부분의 애플리케이션 개발에 공통적 쓰인다고 했지요? 왜 그렇죠? 지금 설명이 다시 원점으로 돌아왔는데.. 처음에 이 개념을 확실히 잡아놓고 가지 않으면, 나중에 좀더 세부적인 사항들을 공부하게 될 때 모든게 헷갈리게 되는까요..확실하게 집고 넘어갑시다.
먼저 MS사가 프로그램을 객체로 세분화할 때, 프로그램의 객체를 무엇무엇으로 정하였죠?
네~~, 모든 윈도우 프로그램에 공통적인 윈도우와 클라이언트 영역 그리고 이를 처리하는 부분과 이들 3부분을 묶어주고 프로그램을 구동시키는 부분해서 프로그램을 이루고 있는 객체를 크게 4가지로 나누었죠? 그렇다면 이 프로그램 객체를 클래스로 구현한 것이 차례대로 C, CView, CDocument, C 클래스 라고 했죠? 그러니 당연히 이들 4개의 클래스들은 모든 윈도우 프로그램에 쓰이겠죠? 너무나 당연한 얘기를 하였습니다. 그렇지만 중요합니다. 제 심정으로는 한번더 반복해서 여러분들이 완전히 기억하고 넘어가셨으면 좋겠지만.. 그러면 너무 지문낭비가 심하겠죠? 통신비도 만만치 않은데...
그럼 앞부분에 MFC가 클래스들의 계층구조로 이루어졌다고 말씀드렸습니다. 그럼 MFC의 개략적인 계층구조와 위의 4개의 기본적인 클래스들이 이들 계층구조에 어디쯤 해당되는지 볼까요?
먼저 여러분들의 이해를 돕기 위해 SDI(Single Document Interface)프로그램을 기준으로 설명하겠습니다. 갑자기 SDI라는 말이 툭 튀어 나오니 당황하시는 분이 있을지 몰라서 아주 간단히 설명하겠습니다.
윈도우 프로그램은 크게 SDI, MDI로 나누어 생각해 볼 수 있습니다. 여러분 프로그램을 사용하다 보면 프로그램 메뉴에 창(Window)이라는 메뉴가 있는 프로그램을 자주 보십니다. 그 메뉴에는 어떤 내용이 들어있냐 하면 바둑판식, 타일 등의 서브메뉴가 있는데요.. 벌써 눈치 채셨겠지만 프로그램내에서 윈도우 창을 여러개 만들거나 불러들여 작업할 때 이런 창들을 정렬시키는 방법이지요? 그럼 윈도우 창이 하나밖에 필요하지 않는 프로그램이 있을까요? 물론 많지요... 위의 프로그램내 2개이상의 새창을 만들거나 불러들일 수 있는 프로그램은 MDI라고 하구요... 단일창만을 가지고 있는 프로그램을 SDI 프로그램이라고 합니다. 사실 창이라고 얘기해도 되지만 템플릿(Templete)라는 말을 쓰더군요.. 똑 같은 얘기지요.. 어쨌든 프로그램이 SDI,MDI라고 나뉠 수 있으니 왠만한 윈도우 프로그램용 클래스를 다 가지고 있는 MFC도 이들을 지원함은 당연하겠지요? 하지만 이들을 뭐 SDI_MFC, MDI_MFC라고 따로 가질 필요가 있겠습니까? 상식적으로 생각해 봐도 그럴 필요가 전혀 없겠죠? 하지만 MDI 프로그램을 만들려면 SDI보다는 뭔지는 모르겠지만 뭔가 MDI를 만들 수 있도록 도와주는 클래스 들이 있을 것 같지 않나요? 예 그렇습니다. 그래서 뭐 그리 차이는 없겠지만 SDI를 기준으로 MFC계층구조와 4개의 기본적인 클래스들을 설명한다고 하면 MDI를 만들 때 필요한 클래스들은 일단 생략하고 설명하겠다는 얘기입니다.. 아시겠죠? 이해가 안가시는 분들은 그냥 모른척하고 지나가세요.. 어차피 알게될.. 알아야 할 얘기니 뒤에 가서 또 나오겠죠?
간단히 MFC계층구조를 볼까요?
Object
| | |
- C - CFile - C
| | | |
| - C - CDC - C
| | | | |
| | - C - C - C
| | | |
| - CDocument - CMenu - C
| |
- CWnd - CDatabase
| |
- C - C
| |
- CView - C
| |
- CDialog - CArray
| |
- C - CList
| |
- 여러 컨트롤 클래스들 - CMap
보신 소감이 어떠십니까? 낯설기는 하지만 복잡하다는 생각은 별로 없으시죠? 사실 위의 계층도는 제가 간단하게 요약한거에 불과합니다. 이를 자세히 보고 싶으신 분은 아래를 클릭하세면 inforview에서 볼 수 있는 내용을 볼 수 있습니다. 그림이 조금은 클테지만 참고하시면 좋을 것 같습니다.
위의 계층도를 간단히 설명하겠습니다. 다음 페이지에서 할까 하다가 저 그림을 보면서 해야 하기 때문에 어쩔 수 없이 다소 길어 지더라도 이번 페이지에서 마무리를 지어야겠네요..
MFC의 클래스를 크게 나누면 CObect에서 상속받은 클래스와 그렇지 않은 2개의 클래스로 나눌 수 있습니다. 위의 제가 그려놓은 계층도는 보시는 것 같이 Object에서 상속받은 클래스들입니다. 상속받지 않는 클래스들을 보시려면 MFC계층도를 참고하세요...
CObject에서 상속받은 클래스를 범주별로 보면 기본적인 애플리케이션 아키텍쳐에 속하는 부분이 C 부분이고 특히 C클래스는 윈도우 메시지와 관련된 클래스입니다. 윈도우가 메시지 구동방식이란 것은 앞에서 설명했지요.. 그와 같은 것을 지원해 주는 클래스라는 얘기입니다. 그 밑에는 CWnd라는 클래스부분이 있네요.. 클래스의 이름을 살펴보면 아시겠지만.. 윈도우(창)과 관련된 클래스이겠죠. 그리고 파일과 관련된 CFile 클래스, 그래픽관련 클래스인 CDC, C가 있고, Database관련 클래스가 두 개 보이는데 CDatabase는 ODBC 데이터 베이스를 지원해 주기 위한 클래스이고, C라는 클래스는 DAO 데이터베이스를 지원하기 위한 클래스입니다. 그다음은 윈도우 소켓프로그램과 관련된 C이라는 클래스가 보이고 그 밑에있는 CArray, CList, CMap은 전부 자료구조(data structure)와 관련된 클래스들입니다. 그리고 맨 오른쪽에 있는 클래스들은 인터넷 관련 클래스들입니다. 그냥 대충, 아주 대충 설명하였는데.. 어떤 범주의 클래스들이 있는지만 보고 넘어가시면 될 것 같습니다. 우리가 여기서 주의 깊게 봐야 할 부분은 앞에서 모든 프로그램에 기본적인 4개의 클래스들입니다. MFC계층도에서 어디쯤에 위치하고 있는지 잘 보십시오.. 빨간색으로 되있으니까 보기 편하실 겁니다.
MFC계층구조와 위 4개의 기본 클래스에 대한 자세한 얘기는 다음페이지에서 하죠.. 넘어갑시다.
10.AFX(Application Frameworks)
앞장에서 대충이기 하지만 4개의 클래스(C,CView,CDocument,C)가 윈도우 프로그래을 만들 때 기본이 되는 클래스라고 얘기했습니다. 그렇다고 이 4개의 클래스를 달달 외워야 하나요? 그럴 필요 있겠습니까?
앞에서 MS사가 프로그램을 어떠어떠한 객체로 나누었다고 했지요? 윈도우틀, 클라이언트 영역(쉽게 허연부분),그리고 눈에 보이지 않는 데이터를 처리, 저장하는 부분 또? 이 세 부분을 묶어서 프로그램을 구동시키는 부분... 뭐 대충 이렇게 나누었다고 했죠? 클래스 이름을 기억하는 것도 중요하겠지만 이름정도야 어딜봐도 나오니 이들 클래스가 어떠한 객체를 표현해 주고 있는지를 알고 있는게 더 중요합니다. 이 4개의 클래스가 윈도우 프로그램을 개발하는데.. 기본골격이 되기 때문에 이를 흔히 AFX라고 부릅니다. 그러니 AFX라는 말에 지래 겁먹을 필요는 전혀 없습니다..
그럼 이들 클래스가 무엇을 표현했는지는 이 정도면 명확하게 알았으니.. 몇 가지 궁금증을 풀어볼까요?
여러분들은 MS사가 프로그램을 객체로 세분화하는 과정에서 왜 프레임윈도우(윈도우틀)와 뷰(클라이언트 영역)를 구분했다고 생각하십니까?
답은 간단히 객체지향적으로 만들기 위해서입니다... 답이 좀 그렇죠? 그럼 생각해 봅시다.. 윈도우프로그램을 실행시켜보면 윈도우(창)이 생기죠? 그럼 이놈이 도대체 어떤일을 합니까? 우리는 마우스를 이용하여 윈도우를 최소화 시킬 수도 있고,최대화 시킬 수도 있습니다. 또 타이틀바 부분을 클릭하여 드래그(drag:끌기)면 윈도우가 움직입니다.. 또 허연부분은 이러거 저런거 막 보여주죠? 물론 워드프로세서,그래픽 도구같은 응용프로그램은 이 허연 부분(클라이언트영역 = 뷰)에서 입력도 할 수 있죠... 그렇지만 결과적으로 입력한 내용을 보여주죠?
윈도우를 최대화 시켰을땐 텍스트로 보여주다가 윈도우를 최소화 시키면 그래픽으로 보여주는 프로그램 보셨어요? 어떻게 보여주는냐는 윈도우 자체를 제어하는 부분과는 아무 연관없이 작동합니다. 즉 윈도우 자체를 제어하는 부분과 데이터를 보여주는 부분이 크게 관여치 않고 독립적으로 움직인다는 얘기입니다. 이는 당연히 프로그램을 만들 때 이런식으로 작동하도록 만들었을테니 이런식으로 작동하겠죠? 이렇듯 객체지향적이라는 것은 하나의 객체(Object)가 철저하게 자기 역할만 한다는 것이죠...
자기가 할 일 예를들어, 윈도우틀(C)가 할 일을 클라이언트 영역(CView)이 대신하게 한다든가... 윈도우틀(C)가 이거저거 다 하도록 프로그램을 만들면 안된다는 얘기죠... '왜 안될까?'는 각자 생각해 보세요..
그럼 두 번째 궁금증을 해결해 볼까요?
어떠어떠한 데이터를 처리한다면 당연히 CDocument클래스가 하도록 만들어야겠죠? 좀더 정확하게 말하자면 CDocument클래스에서 파생된 C(이름은 프로그래머 맘데로지만요..기반클래스가 무언지를 시각적으로 알기위해서 흔히 이렇게 작명합니다)를 상속성과 오버라이딩등을 이용하여 만들어야겠죠.... 그런데.. 왜 도큐먼트와 뷰를 구분했을까요?
만약, 처리된 데이터를 보여주는 방법이 하나뿐이라면 궂이 나눌이유는 없을 것 같습니다... 그러면 이 뷰와 연결된 도큐먼트가 무엇인지를 알 필요도 없이 같은 클래스내에서 간편하게 만들 수 있을 것 같지 않나요? 하지만 우리는 멀티미디어 시대에 살고 있습니다.. "안녕하세요?"라는 데이터를 처리하여 보여주는 방법이 그냥 텍스트로만 "안녕하세요?"뿐이겠습니까? 아니죠... 음성으로 "안녕하세요?"할 수 도 있고, 아니면 글자애니메이션(예를 들어 "안녕하세요?"화면을 마구 돌아다닌다든지..)등등 표현할 수 있는 방법은 수도 없이 많습니다... 엑셀 같은 프로그램에서도 같은 입력내용으로도 데이터시트(sheet)로 보여줄 수도 있고 그래프(graph)로도 보여줄 수 있죠? 이는 어떻게 가능할까요? 이는 하나의 도큐먼트가 여러개의 뷰를 가질 수 있어야 가능한 얘기입니다... 그러니 도큐먼트와 뷰를 다른 객체로 나눠나야..이를 실현할 수 있을 거 아닙니까... 또 다른 이유 중에 하나는 이런저런 기능을 많이 가진 클래스보다는 단순한 클래스로 만드는게 훨씬 더 효율적이라는 측면도 조금은 있습니다.. 어찌됐건 간에.. 이 나뉘어진 객체(Object)들은 철저히 역할분담을 하여 자기역할만 충실하게 합니다.. 자기가 해야 할 일을 않한다거나... 다른 객체가 해야할 일을 자기가 하는 일 따위는 있어선 안돼죠.. AFX는 다음 장에서 자세히 다루겠지만.. 기본정신은 그렇습니다.. "철저한 역할분담 및 성실한 역할 수행" 무슨 캠페인 같군요...
짧은 내용을 설명하면서 객체와 클래스라는 말이 나왔습니다... 아직도 혼돈 되십니까? 노파심에 다음과 같이 정리하였습니다.
객체(Object)
MFC 클래스(Class) =AFX
윈도우틀 생성,제어 부분
C
클라이언트영역(뷰) 부분
CView
데이터 처리,저장 부분
CDocument
위세 개 엮어 프로그램구동 부분
C
컴퓨터를 켜시고 비주얼C++을 실행시키세요.. 강좌는 VC++6에 맞춰하겠지만..VC++5를 가지고 계신 분들이 상당히 많으시기 때문에 예제나 실습등은 공통되는 VC++5로 하겠습니다... 괜찮으시죠?
자~ 실행이 됬으면..ctrl+N이나 [파일]메뉴에서 [new]를 클릭하시면 창이 나옵니다.. 앞에서도 한번해봤으니 벌써 두 번째 인데요.. 프로젝트명을 원하시는데로 적으세요.. 저는 My라고 하죠..그리고 [프로젝트]탭에서 MFC (exe) 선택한다음 Ok! 그러면 영어로 뭐라고 물어보죠? "What type of Application would you like to create?" 어떤 타입에 응용프로그램을 만들꺼냐라고 묻는거죠... 밑에 라디오버튼있는 곳을 보면 Single Document, Multiple Document, Dialog based라고 묻죠? 이게 전에 설명한 윈도우 응요프로그램 유형인 SDI로 할꺼냐 MDI로 할꺼냐를 묻는 것입니다. 물론 세 번째 항목은 Dialog based라고 되있지만 Dialog based는 사실 별거 아니니 나중에 꽤 나중이겠지만.. 예제를 통해서 설명해도 전혀 무리는 없습니다..보면 다 아니까요..
그럼 우린 우선 SDI로 갑시다... 그리고 finish를 클릭하고 ok한번 클릭하면 짜잔~~~~워크스페이스(왼쪽 트리형식의 창)에 뭐가 막 나오죠? (앞으로 클래스뷰)탭을 보면 라는 말그대로 나의 클래스들이 있죠?
+모양된건 다 클릭하여 전체가 다 보이도록 해보십시오..
여러분이 한것에 비해 너무 엄청난 클래스들과 멤버변수, 멤버함수들이 만들어져 있습니다.
찬찬히 살펴보세요.. 먼저 C라는 클래스가 보이죠? 별거 아닙니다..만들제품 제품정보등을 사용자들에게 보여주기 위한 것일 뿐이지 프로그램의 대세에는 전혀 영향을 미치지 않으니 무시해 버리세요.. 그다음에는 C라는 클래스가 보이시죠? 그럼, 이 클래스의 생성자함수와 소멸자 함수를 찾아보세요.....
역시! 클래스명과 같은 이름의 함수가 생성자 함수니까..C()이 생성자 함수이고 ~C()함수가 소멸자 함수지요? 그럼 생성자함수인 C()을 더블클릭해 보세요..오른쪽 창에 뭐가보이죠? 뭐가 보입니까? 다음과 같이 되있을겁니다.
C::C()
{
// TODO: add member initialization code here
}
C::~C()
{
}
에게~~~안에 아무내용도 없네요.. 이부분은 여러분들이 짐작했다시피 my.cpp라는 소스파일에 있는 C의 정의부분 즉 멤버함수의 정의부분중 생성자함수와 소멸자함수의 정의부분입니다.. 아무내용도 없다는 건 아무기능도 하지 않는다는 얘긴데 뭐하러 이렇게 수고스럽게 가 만들어 두었을까요? 꼭 기억하세요!!!! 비주얼C++를 공부하다보면..아니 더 정확히 말해서 MFC를 공부하다보면..음 이 클래스는 이러 이러한 일을 하는 클래스군..쩝..이 멤버함수는 이러이러한 일을 하는군..쩝..그렇군..쩝.. 하게됩니다.. 그런데 초보자들은 특히, C를 조금 할줄 아시는 분들은 입문서등에서 예제등을 통해서 설명하는 부분에서 책에서 하라는 데로 부분부분 이해하고 직접 코딩도 하고 컴파일해서 실행파일 만들어 실행시켜보면 다 잘됩니다..겉보기엔 뭔가 되는 것 처럼 보이지만..이런일들을 계속해 나갈수록 점점 깊이 있는 얘기를 접할수록 MFC라는 놈이 점점 알 수 없는 존재가 되버립니다.. 그중 가장 큰 이유가 "나는 별로 한게 없는데..실행해보면 정말 엄청나다"는 것입니다.. 여러분들도 지금 my라는 프로젝트를 F7,F5차례로 눌러 실행시켜 보세요... 기가 막힐 노릇이죠..그런데.. 처음에는 세세한 부분까지 신경을 안쓰니까 잘 모르시겠지만.. 세세한 부분들을 생각해보면 정말 알 수 없거든요.. 예를 들어 실행을 시켜보면 윈도우가 생성되고 메뉴도 있고 툴바도 있고 상태바도 있습니다. [File]메뉴에 [open]을 클릭하면 파일열기 공통다이얼로그 박스가 나옵니다.. 파일을 선택해도 불러지지는 않지만.. 대충이런식으로 보면 음~~~ 윈도우가 메시지 구동방식이라고 했으니 여길 클릭하면 이 메뉴가 클릭됐다는 메시지가 발생하고 그러면 프로그램이 이 메시지를 받아 공통다이얼로그 박스가 뜨도록 설계한거군...라고 생각이 들면서 자신감이 생깁니다.. 하지만, 툴바를 클릭해서 다른곳으로 드래그 시켜보세요.. 툴바가 띠어졌다..붙어졌다 하죠? 윈도우도 최소화/최대화단추를 이용해서 크게 작게 또는 모서리 부분으로 마우스 포인터를 가져가 임의의 크기로 만들 수도 있습니다..
더 세세한 부분까지 얘기해 볼까요? 마우스가 윈도우창 위를 지나면 마우스가 윈도우 위를 지나가고 있다는 메시지가 수도없이 많이 나오겠죠? 이건 또 어떻게 처리되겠습니까? 마우스포인트를 움직이면 마우스포인터의 잔상이 남지 않고..지나간 자리의 마우스포인터 모양은 없어지고 다시 허연 클라이언트 영역이 보입니다.. 이런 화면복원등도 프로그래머가 직접해줘야 하는거지요.. 이런저런 생각을 마구하다보면 단지 My만을 입력한 우리는 바보가 된 느낌이 들죠... 에게 농락당한 느낌이 든다니까요...정말로요...
너무 얘기가 쓸데없이 장황해졌는데...얘기하고자하는 바는 간단합니다.. MFC를 학습할 때는 방향과 포인트를 잘 잡아야 한다는 것입니다..
그냥 클래스인 경우 기능이 뭐고 어떤 멤버가 있고, 멤버함수인 경우 어떤 기능을 하고 어떤 인자를 받고 어떤 타입의 데이터를 넘겨주는지에 관심을 집중시키면 안된다는 겁니다.. 물론 이런 것들이 중요한 것은 말할 것도 없겠지만.. 이런식으로 학습하는 것은 C 라이브러리를 공부하는 것과 다를 바가 없지 않습니까? 그러니 클래스인 경우 계층구조상 어디에 위치하고..기반클래스가 뭐이며..등의 클래스의 상속성과 관련된 것들에게 집중을 시키고.. 함수의 경우에는 이 함수가 언제 호출되는 함수인가에 관심을 먼저 집중하여야 한다는 것입니다.. 물론 저도 이러한 관점에서 설명해 나가겠습니다..
10.AFX(Application Frameworks) (계속2)
그럼 내용도 없는데데..왜 굳이 함수 부분이 정의되있을까요...
그것은 아무일을 하지 않더라도 호출은 된다는 말입니다..
가 바보가 아닌이상 사용되지도 않는 함수를 정의하겠습니까?
클래스를 만들기만 해놓으면 아무소용 없다고 했지요? 클래스의 인스턴스를 만들어 이 인스턴스를
사용해야 클래스를 만든 보람이 있는 것입니다..
함수도 마찮가지예요.. 만들어 놓기만 하면 뭐하겠습니까.. 사용(호출)이 되지 않으면 아무소용
없는거 아닌가요?
생성자함수와 소멸자함수도 현재로선 아무일도 하지 않지만 프로그램이 실행하는 과정에서
호출이 된다는 말입니다...
그럼 이 성자와 멸자는 언제 호출될까요? 앞에서 설명했는데.....
생각안나시면..다시 앞부분을 보세요..여기서 다시 장황하게 설명하는건 무리인거 같습니다.
생성자함수는 클래스의 인스턴스가 생성될 때 호출되고 소멸자함수는 인스턴스가 소멸될 때
호출되고 클래스의 인스턴스는 지역변수,전역변수,정적변수중 어떤 변수로 선언되었냐에 따라
생성과 소멸시기가 달라진다고 하였습니다..
만약 우리가 안이 텅비어 있는 생성자함수에 이런저런 기능을 넣어준다고 한다면..
이는 "이 클래스의 인스턴스가 무슨 변수로 선언되었으니까 생성자 함수가 프로그램중 이쯤에서
호출될테니 여기다 이쯤에서 해야할 일들을 넣어주자"라는 생각을 먼저해야 한다는 것입니다..
<꼭꼭꼬옥~ 기억해야할 내용>
일반적인 함수는 함수를 정의하는 부분이 있으면, 반드시 반드시 그 함수를 호출하는 부분이
있게 마련입니다.
그렇기 때문에 우리가 어떤 기반클래스의 어떤 멤버함수를 오버라이딩하여 수정했다고 한다면
우리는 이 파생클래스의 오버라이딩된 멤버함수를 호출하지 않습니다..
왜냐하면, 이 오버라이딩된 함수는 기반클래스에서 호출되고 있기 때문입니다.
MFC를 이용한 프로그래밍이 다 이런식으로 이루어집니다..
즉 MFC는 별다른 일을 하지 않는 함수라도 이럴 때 호출되는 함수, 저럴 때 호출되는 함수등을
각종 상황에 따라 이미 다 만들어 두었다는 얘깁니다..
이렇게 각종상황에 호출되는 함수들이 다 만들어져 있다는 것은 어떠한 상황이 되면 그에
해당하는 함수를 호출하는 프로그램의 틀이 이미 다 만들어져 있다는 것입니다...
그럼 우리는 뭘하면 되겠습니까?
위의 내용이 우리가 MFC를 공부할 내용입니다...여기에 초점을 맞추세요...
그럼 흥분을 가라앉히고...다시 클래스뷰를 봅시다..
각각의 클래스 안에는 멤버함수와 멤버변수가 있습니다..어떤 것이 멤버함수이고 어떤 것이 멤버
변수인지는 멤버앞에 있는 아이콘 모양만 봐도 알 수 있습니다.보라색 정육면체가 함수/ 하늘색
정육면체가 변수를 가리킵니다.. 그리고 멤버 앞에 있는 열쇠는 protected난 private로 선언된
변수들이니 클래스내부에서만 참조될 수 있는 변수들이죠...
그런데 C클래스 말고 어떤 클래스가 또 있지요?
예~~ 보시는 것과 같이 C, C, C가 있군요..
그곳에도 전부 생성자 함수와 소멸자 함수가 있죠...
놓치기 쉬운 한가지 Global(전역변수)밑에 뭐가 있습니까?
theApp라는게 있는데.. 이게 도대체 뭡니까?
이것은 C클래스의 인스턴스입니다.이젠 인스턴스까지 만들어 놨군요...
이설명은 다음 페이지에서 하도록 하겠습니다..
그럼 이제 하나하나 클래스를 살펴볼까요?
<노파심>
혹시 C,C,C,C,클래스를 MFC라고 생각하시는 분 있으면 손들어 보세요..
어~~ 꽤 여러분들이 그렇게 생각하시는군요...쩝..
위의 네 개의 클래스들은 각각 MFC의 C,C,CDocument,CView클래스에서 상속받은
파생클래스들입니다..
상속받는 이유가 뭐지요? 그대로 사용할꺼면 그냥 MFC에 있는 클래스를 그대로 사용하면 되는데..
우리가 뭔가 더 넣을려고 하는 거지요..
실행시켜봐서 알겠지만 우리가 간단한 메모장을 만들고자 한다면, [File]메뉴에 있는[Open]을
클릭하면 파일열기 공통다이얼로그 박스가 나오는데..지금은 파일을 열 수 없지만 우리가 파일을
읽어 뷰영역에 보여주는 기능을 넣어주어야겠죠?
이런 기능을 넣어주려면 MFC를 그대로 사용하면 안되니까 상속받아 파생클래스를 만든 것입니다..
그러니 우린 앞으로 멤버변수도 추가하고 함수도 추가하고 기반클래스의 멤버함수를 오버라이딩
하기도 할꺼 아닙니까?
그리고 우리는 아직 아무 인스턴스도 만들지 않았습니다.. 그것은 곧 클래스를 사용하지 않았다는
얘기죠...
아니 클래스를 사용하지도 않았는데..어떻게 실행이 되냐구요?
당연하신 말씀입니다..
하지만 우리가 만들진 않았지만 프로그램시작때 생성되서 종료시 소멸되는 전역변수 theApp란
C의 인스턴스를 가 만들어 놨지 않습니까....
이 인스턴스 때문에 실행이 되는 것입니다..
이에 대한 설명은 차차 할테구요.. MFC에서 상속받은 파생클래스라는 점 잊지 마세요..
MFC의 클래스, MFC에서 상속받은 파생클래스, 인스턴스를 헷갈리기 시작하면 죽도밥도 않됩니다...
Visual C++ 6 강좌(025)
10.AFX(Application Frameworks) (계속3)
AFX의 베일을 벚겨(?)봅시다..
먼저 눈에보이지 않는 객체를 구현한 클래스중에 프로그램을 구동시켜주는 C클래스를
살펴보겠습니다.
1.C
C,CDocument,CView클래스를 묶어 프로그램을 구동시키는 역할을 하는 클래스라고 벌써
설명하였지요?
그럼 먼저 위치확인!!
어디에 있습니까? 아~~뭐하세요..찾아 보셔야죠...직접눈으로 확인하는 습관을 먼저!!
그럼 간략하게 적어보겠습니다.
CObject-> C-> C->C
이것은 C가 어떤 클래스에서 파생되었는지를 보여주고 있습니다.
역시 Class의 대부인 CObject에서 파생된 클래스지요? 물론 CObject에서 파생되지 않은 클래스도
있습니다. 그렇지만 그것은 일부이고..앞으로 설명될 대부분의 클래스는 CObject에서 파생된
클래스들입니다.
C는 CObject에서 파생되었기 때문에 CObject의 내용이 다 있겠죠?
이렇게 따져보면 C는 CObect,C,C의 모든 내용을 다 가진 셈이죠...
그럼 이 클래스가 어떤 일을 하는지 볼까요?
제가 대충 프로그램을 구동시키는 역할을 한다고 하였습니다.
모든게 시작과 끝이 있듯 프로그램에도 시작과 끝이 있습니다.(무슨 철학서에 나오는 문장같군요)
물론 시작과 끝사이에도 뭔가가 있죠...
그럼 프로그램을 구동시킨다는 얘기는 프로그램의 시작과 끝 그리고 그사이에도 일을 한다는 얘기
입니다..
그래서 이 클래스는 프로그램의 시작과 종료를 담당하고 프로그램이 실행되는 동안(즉 종료되기
전까지)메시지를 뿌려줍니다...
그럼 정리!!
메시지를 뿌려줌
앞에서 우린는 를 이용하여 아무일도 하지 않는 윈도우 프로그램을 만들었습니다.
프로그램을 시작하면 윈도우(메인프레임윈도우)뜨지요?
이것을 C 클래스가 한다는 얘기지요...
그리고 우리가 메뉴를 클릭하면, 메뉴가 활성화(색깔이바뀌는 것을 고상한 말로 활성화 어쩌구
하는 거죠..)되지요? 이것은 어떻게 되는 거죠? 예~~ 메뉴를 클릭하면 마우스가 클릭되었다는
메시지가 발생하고 이에 대응하여 호출되는 함수(메시지 핸들러 함수)가 호출되어 이렇게 저렇게
수행을 하는 거죠... 이 윈도우 메시지를 프로그램에게 알려주는 것도..C가 하는 역할
입니다..
또 어떤식으로 프로그램종료시까지 메시지를 뿌릴까요?
다음과 같이 프로그래밍하면 되겠죠....
while(윈도우메시지 != WM_QUIT)
{
메시지를 뿌려라~~~
}
뭐 대충 이런식으로 하면 되겠죠.. 윈도우 메시지가 WM_QUIT가 아닐 때까지 메시지를 뿌려라..
일종의 무한루프지요?
우리가 프로그램을 만든다면 이 C클래스가 필요하겠죠?
C로 충분하다면 그냥 이 클래스를 쓰면 되지만... 우리는 뭔가 바꾸고자 합니다..
당연히 그럴테지요.. 메인프레임 위도우가 뜨기전에 뭔가 하고 싶다면 이 클래스에 그 뭔가 하고
싶은 일을 넣어주어야 할테니까요..
그래서 C라는 이름으로 파생클래스를 만든거 아닙니까...물론 가 알아서 해주긴
했지만..
그리고 앞에서도 얘기했지만 C의 인스턴스가 있죠? 어디에 있죠? 워크스페이스에서 찾아
보세요...Global(전역변수)부분에 있죠?
클래스는 만들기만 해놓아서는 아무 소용이 없죠...사용을 해야 쓸모가 있는것입니다.
그래서 theApp라는 인스턴스를 만들었습니다. 이 인스턴스가 없으면 컴파일을 시켜도 아무것도
없겠죠?..전역변수로 된 이유는 프로그램이 시작하면서 이 인스턴스가 생성되고 종료될 때 소멸
되게 하기 위한거죠.. 계속 반복하니까 이젠 지겨우시죠?
C의 기능을 단순화 시키면 시작-메시지뿌리고-종료 식으로 생각할 수 있습니다..
실제로 이를 하는 멤버함수는 (), Run(), ()입니다
그래서 C는 인스턴스가 생성되면서(프로그램이 시작할 때) 이 멤버함수를 위의 차례로
호출합니다..
그럼 각각의 멤버함수가 하는 일은 무얼까요? 얘기하지 않아도 아시겠죠?
위에 벌써 설명다 했는데...
()는 인스턴스가 생성될 때 해야 하는 일들을 해야겠죠?
이 인스턴스가 생성될 때 해야 하는 일이란 프로그램을 초기화하는 각종 루틴을 말하는 겁니다.
이중 제일 중요한 것은 CView,CDocument,C클래스를 묶는 것이지요..
그럼 Run()은 WM_QUIT메시지를 만날 때까지 메시지를 뿌려주는 일을 하겠지요?
그리고 ()는 프로그램이 종료하기전에 실행되는 함수로 여러 정리루틴을 가지고 있
겠죠....
그래서 C가 하는 일을 간단하게
() -> Run() -> ()로 말할 수 있습니다..
Visual C++ 6 강좌(026)
10.AFX(Application Frameworks) (계속4)
이제 가 만들어준 C의 파생클래스인 C를 잠시 보겠습니다.
C는 C가 가지고 있는 멤버를 다 가지고 있습니다.. 그래서 같은 기능을 합니다..
너무나 당연한 얘기입니다..
그렇다면 클래스를 상속받을 쓰는 문장만 써주면 되겠군요...아래와 같이요..
class C : public C{};
똑 같은 기능을 하니 안에 아무것도 쓰지 않아도 되겠죠... 그럼 확인해 봅시다..
클래스의 선언부분이니 C가 선언되어있는 헤더파일을 보면되겠죠?
헤더파일은 어떻게 봅니까?
워크스페이스밑에 탭을 보면 라는 탭이 보입니다. 이를 클릭하면 Source File,
Resource Files, Header Files 항목이 있습니다.
그냥 보면 알겠지만 소스파일은 cpp확장자를 가진파일들을 모아놓은 항목이고, 리소스 파일은
그림,아이콘,리소스(메뉴바,툴바등)등을 모아놓은 항목이고, 헤더파일은 말그대로 헤덯파일을 모아
놓은 항목입니다..
그럼 헤더파일을 클릭하면 확장자가 h인 여러 파일이 나오는데요...어느 헤더파일에 선언되어
있을까요?
눈치채셨겠지만..My.h라는 파일에 있습니다.. 이를 클릭하면 C를 선언하는 부분이 나옵니다.
기름끼 좍빼서 보면 다음같이 되 있지요..
class CZApp : public C
{
public:
CZApp();
public:
virtual BOOL ();
};
안에 뭐라고 적혀 있네요...이는 가 알아서 넣어 둔건데.. 이를 보면 CZApp()라는 함수
를 오버라이딩 해서 쓰라고 되 있습니다..(주석부분에요..)
그럼 안에 어떻게 오버라이딩되있는지 볼까요?
멤버함수의 내용을 보려면 클래스의 정의부분(=멤버함수의 정의부분)을 보면 되는데...어디서
보면 되겠습니까? cpp확장자를 가진 소스파일을 보면되겠죠?
r.cpp를 클릭해서 보면 아래와 같이 되있습니다.
CZApp::CZApp()
{
// TODO: add construction code here,
// Place all significant initialization in
}
아무 내용도 없네요.. 우리보러 알아서 하라는 얘기죠..알아서 하라는 말은 여기에 뭔가 넣으려면
넣고 말라면 말라는 얘기입니다..그럼 이 함수가 프로그램중에 언제쯤 호출되는 지를 봐서
그 즈음에 해야할 일을 여기에 넣어주면 되는 거죠... 그런데 주석문을 보니 어떤 것들을 넣어야
되는지 대충 알 것 같군요... 영어를 못하면 여러 가지로 고생이죠..
지금 이함수에 대해선 자세히 하지 않겠습니다...지금은 이 함수를 쓸일이 거의 없으니까요..
자~~그럼 파생클래스인 C의 또다른 멤버함수를 볼까요? 여기에 함수가 있군요
이 함수는 기반클래스인 C에서 정의된 함수이기 때문에 써줄 필요가 없을 것 같은데..
써준걸 보니 내용을 수정또는 추가 즉, 오버라이딩해서 쓰라는 친절한 의 짓이군요..
그럼 혹시 가 알아서 적당한 내용까지 넣어놨을지도 모르니까 한번 볼까요?
이 함수 정의된 부분을 찾았습니까?
어떻게 되있나요? 다소 복잡해 보이지요?
또 기름기 쫙빼서 아래와 같습니다.
[My.cpp]
#include "stdafx.h"
#include "My.h"
#include ".h"
#include ".h"
#include ".h"
C::C()
{ };
C theApp;
BOOL C::()
{
C *p;
p = new C(IDR_MAINFRAME,RUNTIME_CLASS(C),
RUNTIME_CLASS(C),RUNTIME_CLASS(C));
(p);
return True;
}
와우~ 뭔말인지 하나도 모르겠군요...
차근히 봅시다.. 위의 #include 문장은 헤더파일을 인쿨루드하라는 얘긴데..
생소한 헤더파일하나가 보이는군요... stdafx.h...
다른건 대충알겠죠? 간단히 예를 들면 .h는 CDocument에서 파생된 C클래스의 선언부분
가진 헤더파일이구요.. 나머지도 마찮가지인에...
stdafx.h라~~~
그런데.. C공부하신 분들은 어디서 많이 본 것같은 모양을 하고 있군요...꼭 "stdio.h"랑 비슷
하지 않나요? 지겹게 나오는 파일이지요.. stdio는 S Input Output의 의미를 가진거였죠?
기본적인 입출력과 관계된 함수들과 상수 매크로 기타등등이 선언된 헤더파일입니다.
예를 들어 화면출력함수인 printf()함수를 사용하려면 반드시 stdio.h파일을 소스파일(***.c)에
포함(include)시켜 주어야 됬었습니다. 만약 인클루드하지 않고 그냥 사용하면 컴파일 에러가
발생합니다..너무나 당연한 얘기였습니다...
이 stdafx.h라는 헤더파일도 C의 stdio.h만큼 중요하고 자주 쓰이는(?) 아니 거의 쓰이는 헤더
파일이니 시간날 때 마다 그 내용을 이해는 안가더라도 눈에는 익숙하게끔 하는 것이 좋을 것
같습니다.. 그렇다고 지금 당자 보실 필요는 없구요...심심할 때요...
stdafx는 S AFX(Application Frameworks)의 약자이구요.. MFC 클래스들의 선언을 비롯해서
상수,매크로정의등 MFC를 이용해 윈도우 프로그래밍을 하는데 필요한 모든 것이 정의되어 있습
니다.. 굉장하지요? 그러니 거의 대부분 인클루됩니다...stdio.h가 C에서 그랬듯이요...
그럼 함수가 어떻게 정의되 있는지 한번 보세요...
생소하기 그지 없습니다..
그런데 모르는 함수 몇 개와 너무나 긴 이름들 때문에 복잡해 보일 수도 있습니다..
하지만 계속 공부하다보면..아니 프로그램을 짜다보면.. 이렇게 자세하게 지은 이름들이 얼마나
도움이 되는지 아실 겁니다..
잔소리하나 더 하자면...함수명이나 클래스명이나 변수명을 정할 때는 그 특징또는 기능등을
자세하게 표현할 수 있는 단어를 만드세요..길면길수록 좋습니다..물론 코딩할 때는 짜증나겠지만
는 프로그램이 시작되면 호출되는 함수라고 했지요? 그래서 여러 가지 초기화 루틴
이 들어가는 데요..
C가 하는일이 AFX클래스들을 묶어주는 일이 있었죠?
이 일을 지금 여기서 하는 것이죠.. C를 그대로 사용한다면 우리가 저렇게 해줄 필요가
없었겠죠..아~~물론 저것은 우리가 해야하는 일을 친절한 가 알아서 해준 것입니다.
따라서 가 없다면 우리가 저렇게 일일이 해주어야 합니다..
내용은 이렇습니다..
C라는 클래스의 인스턴스를 생성시키려고 하는데 포인터를 이용하였고..
즉 C 타입의 포인터(int형의 포인터-int *pointer식으로 말이죠..)를 선언하고
도큐먼트 클래스, 프레임윈도우 클래스, 뷰클래스로 각각 우리가 파생시킨(가 알아서
파생시켜준) C, C,C 클래스를 사용하겠다는 것을 포인터인 p를
이용하여 C 클래스에 설정한다음에 (C의 멤버함수)를
호출해서 C클래스와 연결시킨 것입니다..
대충 어떤 내용인지 아시겠죠?
아주 쉽게 얘기해서 AFX클래스로 C, C, C를 사용하겠다 그리고 이를
C클래스와 연결시켜 묶어서 프로그램을 구동시키겠다는 내용이지요..
몇줄 안되는 문장이지만 정말 정말 대단한 일을 하고 있습니다..
우리는 여기서 C클래스와 여기서 파생된 C라는 클래스를 완전히 파헤치자는게 아닙
니다.
개념적으로 어떤 일을 하고, 어디에 위치하고,C라는 클래스를 이용하되 조금 수정또는
추가하여 사용하고자 C라는 파생클래스를 만들었고..이 모양이 대충 어떻게 생겼는지만
알면 됩니다..
거의 2페이지에 걸쳐 설명하였는데..한번 읽어보시고 바로 위의 말슴드린대로 나름대로 정리해
보세요.. 자기 나름대로 정리해 보는 것이 아주 중요합니다.. 처음 개념을 잡는데는 말이죠..
그럼 C와 CView클래스를 보기전에 이들의 기반클래스인 CWnd클래스를 보겠습니다...
갑시다...
Visual C++ 6 강좌(027)
10.AFX(Application Frameworks) (계속5)
2.CWnd
우리가 다음으로 살펴볼 AFX클래스는 C,CView인데..이들의 상당부분의 중요한 기능들이
기반클래스인 CWnd클래스에서 상속받은 것 이기 때문에 이를 먼저 살펴보는 것이 좋을 것 같습
니다..
그럼 위치 확인!!
CObject -> C -> CWnd
CWnd도 모든 클래스의 대부인 CObject에서 파생되었군요...
전에 C도 C이라는 클래스에서 상속받은 파생클래스였는데 CWnd도 마찬가지이군요..
그럼 C은 어떤 내용으로 되어 있을까요?
C에 대해선 MFC시작하기 부분에서 간략하게나마 말씀드렸는데요..
이 클래스는 윈도우 메시지를 다루는(핸들링하는)기능들이 정의되어 있습니다..
그렇다고 우리가 메시지를 핸들링하기위해 이 클래스에서 상속받은 파생클래스를 직접 만들 필요가
있을까요? 사실 프로그래밍을 할 때 그러한 일은 거의 없습니다...
왜일까요?
그 이유는 윈도우 프로그램을 만들 때 기본이 되는 핵심 클래스(Key Class)인 AFX 클래스들,
즉, C,C,CDocument,CView클래스들은 모두 이 클래스에서 상속받은 파생클래스들이기
때문입니다.. 즉 C 모든 AFX클래스들의 기반클래스라는 말이죠...
따라서 모든 AFX클래스들은 C의 메시지 핸들링 기능을 사용할 수 있기 때문에..
메시지 핸들링을 하기위해 C에서 직접 상속받은 파생클래스를 별도로 만들 필요가 없습
니다..
아래에 에 있는 C클래스의 설명부분 중 일부입니다. 참고하세요..
여기에 message-map architecture라는 다소 생소한 말이 나오는데.. 이는 메시지핸들러 함수를 다루는 부분에서 자세하게 설명하겠습니다...
C is the base class for the Microsoft Foundation Class Library message-map architecture. A message map routes commands or messages to the member functions you write to handle them. (A command is a message from a menu item, command button, or accelerator key.)
Key framework classes derived from C include CView, C, CDocument, CWnd, and C. If you intend for a new class to handle messages, derive the class from one of these C-derived classes. You will rarely derive a class from C directly.
참고로 새로운 클래스가 나왔을 때 이에 대한 설명을 에서 보고 싶으시면
워크스페이스의 탭을 클릭하여 다음과 같은 순서로 찾으면 됩니다...
Developer Products -> Visual C++ -> Microsoft Foundation Class Reference
-> Class Library Reference ->
이제 CWnd 클래스에 대해서 자세히 알아볼까요?
CWnd클래스는 크게 두가지 부분으로 나누어 생각해 볼 수 있습니다..
: 윈도우의 크기나 위치, 모양, 상태등을 제어하기위한 기능이 있는 부분
: 윈도우에서 발생하는 메시지를 처리하기위한 메시지 핸들러 함수관련 부분
첫번째 부분을 위한 멤버함수는 약 100여개정도가 되구요..두번째 메시지 핸들러 함수라 불리는
맴버함수는 약 200여개가 있습니다..정말 엄청나게 많은 함수지요?
이를 다 보려면 며칠밤은 꼬박 새워야겠군요...큰일났습니다.. 어떻게 하지요?
이제는 눈치들이 빠르셔서 아무리 이렇게 얘기해 봐야 지레 겁먹는 분들은 거의 없는 것 같군요..
먼저 윈도우 크키나 위치, 모양, 상태등을 제어하는 부분은 특별한 경우가 아니면 그대로 사용
하는 경우가 대부분이 이기 때문에 지금으로선 가 만들어 놓은 부분을 그대로 사용해도
전혀 무리가 없습니다..
해봤잖아요...최소화/최대화도 되고 임의크기로 변경도 할 수 있고..이동도 되구요..다 보셨죠..
문제는 메시지 핸들러 함수인데요...200개가 넘는 메시지 핸들러 함수가 윈도우 프로그래밍에서
상당히 중요한..정말 중요한 함수이지만(당연하지요..윈도우가 메시지 구동방식이니..)..지금 당장
그 많은 함수를 다 볼 필요는 전혀 없습니다...자주 발생하는 메시지를 위주로 그에 대응하여
호출되는 메시지 함수를 차근차근 보면 되니까요..메시지 함수도 한 20개 정도의 이름과 내용을
간단하게 머리속에 넣고 있으면 프로그래밍하는데는 별 불편없습니다..
물론..많이 알고 있으면 더 좋게지만요...저는 머리가 나빠서요...
그럼 메시지 핸들러 함수에 대해 살펴 볼까요?
1) 메시지 핸들러 함수(Message Handler function)
메시지 핸들러 함수는 간단하게 메시지를 핸들링하는 함수입니다..
참 웃긴 설명이란 생각이 드는 군요...하지만 이게 정답입니다..
핸들(Handle)이 영어로 '다루다,쓰다,처리하다'라는 뜻이더 군요(동아 프라임 사전)..
그럼 메시지 핸들링이란 것은 '메시지를 다루다', '메시지를 처리하다'란 뜻이 되겠죠?
그러니 메시지 핸들러 함수가 메시지를 핸들링하는 함수라는 얘기입니다..
그런데 이 메시지란 것이 어떤 것이지요? 예...윈도우에서 발생되는 메시지를 말하는 것이지요..
윈도우가 메시지 구동방식으로 돌아간다고 했으니.. 메시지 핸들러 함수를 좀더 자세히 말하자면
윈도우에서 발생하는 메시지를 처리(핸들링)하는 함수라는 말입니다.
어려울 것 하나 없죠? 그럼 윈도우에선 어떠한 메시지가 언제 발생할까요?
메시지가 언제 발생하느냐는 순전히 사용자(user)가 어떤 행동을 하느냐에 따라 달려 있습니다..
사용자가 우두커니 컴퓨터를 보고 있다면 잠시나마 아무 메시지도 발생하지 않지요..
그러나 사용자가 마우스를 잡고 조금만 움직여도 곧바로 마우스가 움직였다는 WM_MOUSEMOVE라는
메시지가 발생하죠..그럼 우리는 프로그램을 짤 때 이 메시지가 발생하였을 때 프로그램이 해야
할 일을 이 WM_MOUSEMOVE함수를 처리하는 메시지 핸들러 함수 함수에다가 해야할 일
을 넣어 주면 되는 겁니다...
그렇다면 함수를 만들었다면 사용즉 호출을 해 줘야 하는데.. 어떻게 하면 되지요?
답은 우리가 더 이상 할 일은 없다는 것입니다.. WM_MOUSEMOVE 메시지가 발생하면 자동적으로
함수가 호출되거든요..그렇게 되 있어요...아주 편리하죠?
프로그래머는 단지 해야 할 일만 메시지 핸들러 함수에 넣어주면 끝나는 것입니다..
또 사용자가 무슨 버튼을 클릭하면 마우스 왼쪽버튼이 클릭되었다는 메시지가 발생하고..
그에 대응하는 메시지 핸들러 함수가 호출될 테니 어떤 버튼이 눌려졌을 때 해야 할 일이 있을 때
이 대응하는 메시지 핸들러 함수에 해야할 일을 넣어 주면 되는 것입니다..
모든게 다 만찬가지예요..아주 아주 쉽습니다.. 적어도 여기까진...그렇죠?
앞에서도 간단히 설명한 것 같은데요..윈도우에서 발생하는 메시지는 전부 WM_어쩌구(대문자)식으
로 되 있습니다.. 그리고 그에 대응하는 메시지 핸들러 함수 이름은 전부 On어쩌구식이 됩니다.
그럼 자주 사용되는 윈도우 메시지가 어떤게 있는지 다음장에서 한번 살펴보죠...
Visual C++ 6 강좌(028)
10.AFX(Application Frameworks) (계속6)
자주 사용되는 윈도우 메시지아 그에 대응하는 메시지 핸들러 함수는 다음과 같습니다..
<표> 자주사용되는 윈도우메시지와 메시지 핸들러 함수
윈도우 메시지
발생상황
메시지핸들러함수
WM_CREATE
윈도우가 생성될(생길) 때
WM_ACTIVATE
윈도우가 (비)활성화 될 때
WM_PAINT
윈도우가 다시 그려져야 할
필요가 있을때
WM_MOUSEMOVE
마우스 움직였을 때
WM_COMMAND
메뉴등으로 명령을 내릴 때
WM_LBUTTONDOWN
마우스 왼쪽버튼 눌렀을 때
OnL
WM_LBUTTONUP
마우스 왼쪽버튼 뗐을 때
OnL
WM_LBUTTONDBLCLK
마우스 왼쪽버튼 더블클릭때
OnL
WM_KEYDOWN
키보드 눌렀을 때
WM_KEYUP
카보드 뗐을 때
WM_SIZE
윈도우 크기가 변경됬을 때
WM_MOVE
윈도우가 이동되었을 때
WM_TIMER
설정된 타이머 시간이 다 되
을 때
WM_DESTORY
윈도우가 없어질 때
보신 소감이 어떻습니까? 너무어려워서 죽겠지요?
이름만 봐도 이 메시지가 언제 발생되는 메시지인지 금방 알 수 있습니다..다른 메시지들도
마찮가지예요..그러니 어려울 것 하나 없습니다...
유심히 보신 분들은 WM_COMMAND 즉, 메뉴같은 것을 클릭했을 때 발생하는 메시지에 대응하는
메시지 핸들러 함수가 없네요...
WM_COMMAND 메시지는 조금 다른 방법으로 처리하는데요..그 이유가 있습니다..
메뉴와 같은 것들은 프로그래머가 직접 만든 것이지요? 물론 우리와 같은 경우에는 가
만든 것이 전부지만요..어쨌든 프로그래머가 직접 만든 것입니다..
프로그래머가 어떤 메뉴항목을 만들지는 아무도 모릅니다..정말 며느~도 모르지요..
그러니 어떻게 각각의 메뉴항목에 대한 메시지를 미리 정해 놀 수 있겠습니까?..
그래서 이런 것을 일과적으로 처리하는 WM_COMMAND라는 메시지를 둔 것이지요.. 이것만으로도
우리는 프로그램을 작성할 때 상당한 도움을 받습니다..
WM_COMMAND 메시지는 메시지가 발생할 때 어떤 항목이 클릭되어 발생했는지에 대한 정보를 가지고
있지요.. 그래서 메뉴항목의 ID를 참조하여 메뉴항목의 어떤 메뉴가 클릭되었다고 하는 추가정보를
같이 보내오기 때문에 우리는 WM_COMMAND메시지가 발생하였을 때 추가정보를 보고 이에 대응하는
함수를 연결시켜 주고 이 함수에 해야 할 일을 넣어 주면 됩니다..
다른 메시지 함들러 함수들보다는 조금 복잡한 것 같군요..그럴 수도 있지만요.. 직접해보면..
정말 별거 아니지요..
여러분이 위의 설명을 보면서 가장 걱정되는 부분이 어떻게 WM_COMMAND라는 메시지가 발생하였을
때 그 추가정보(메뉴ID같은거)를 보고 우리가 직접만든 함수와 연결시켜고 이를 어떻게 호출해야
되는지에 대한 부분일 것입니다.
하지만 걱정할 필요는 하나도 없습니다.. 이와 같은 일을 Class Wizard(앞으로 클래스위저드)가
아주 간편하게 해주거든요..우리는 각 메뉴마다 ID를 만들어 놓고 클래스위저드를 이용하여
이에 대응하는 함수를 만들어주면 되거든요..그러면 그 메뉴항목이 선택되면 WM_COMMAND 메시지가
가 발생하고 이에 대응하는 함수(우리가 직접만든 함수)가 자동으로 호출됩니다..
끝내주죠?
그러면 클래스위저드를 사용하는 방법만 알고..이에 익숙해 지기만 하면 되겠군요..
클래스 위저드의 사용방법은 AFX강좌가 끝나고 우리의 첫 번째 프로그램을 만들면서 자세하게
알려드리겠습니다.. 먼저 말씀드리지만 사용하기가 편리하기 그지 없으니 전혀 걱정하지 마세요..
윈도우 메시지(WM_~~)가 발생하면 자동으로 이에 대응하는 메시지핸들러 함수가 호출된다고 하였
는데..어떻게 자동으로 호출될 수 있을까요? 궁금하지 않으세요?
사실 모르셔도 지금으로선 상관없을지도 모릅니다..
그래도 메시지 핸들러 함수를 앞으로 수도없이 정말 지겹게 사용하게 되는데..그때 마다 이런
궁금증을 품고 있다면 정신건강에 해로울 수 있으니..간단하게 설명드리겠습니다...
2) 메시지 핸들러 함수의 호출원리
제목이 거창하군요...하지만 그 원리란게 워낙 간단해서 설명드리기가 민망하기 그지없습니다..
윈도우가 메시지를 발생하면 이런이런 메시지가 발생하였다고 프로그램에게 알릴텐데..
이 메시지라는게 시도때도 없이 수도없이 발생하는 것이거든요.. 귀찮기 그지없지요..
프로그램도 뭔가 해야 할 일이 있는데..하고 있는 도중에 이런 메시지가 계속 나오면 그때마다..
즉시 그 메시지에 대한 메시지 핸들러 함수를 호출할 수 있을 까요?
그래서 모든 윈도우 프로그램은 이 메시지들을 일단 저장해 놓을 수 있는 일종의 메모리 공간을
가지고 있는데요..그걸 메시지 큐라고 부릅니다..
그래서 첫 번째 들어온 메시지를 첫큐, 두 번째 들어온 메시지를 두 번째 큐라고 하고 이 메시지
가 발생하는 간격이 길면 '큐거리가 길다'라고 하구요 간격이 짧으면 '큐거리가 짧다'고 합니다.
그리고 큐거리가 지나치게 길거나 짧으면 메시지를 처리하기 곤란하니 적당한 큐거리를 가지는
습관을 가져야 합니다...
그리고 들어온 모든 메시지를 한번에 처리하면 '한큐에 끝냈다' 또는 '스킬'이라고 하고...
이럴땐 아무말 하지 말고 종료버튼을 누르고 유유히 손을 씻고 밖으로 나오면 아주 멋지죠..
절대도 아무말도 하지 않아야 합니다.. 그래야 멋있다니까요...
죄송합니다.. 너무 지나쳤습니다..여러분의 귀한 시간을 뺐었습니다.. 용서하십시오...
다시 제정신을 차리고...
발생하는 메시지를 일단 저장해 놓는 메모리 공간을 메시지 큐라 하였죠?
혹시 자료구조를 보신분들은 아시겠지만..자료구조에는 스택,큐,데크,리스트등 여러 가지가 있습
니다..그래서 FIFO(먼저 들어간 놈이 먼저나온다),FILO(먼저들어간 놈이 나중에 나온다)등의
말이 나오는데요.. 스택(Stack)이 FILO에 해당하구요.. 큐(Que)는 FIFO에 해당합니다..
이런 것은 아직 알 필요가 없지만 혹시나 다른 책에서 이런 설명이 없어서 조금이나마 궁금해하신
분들을 위해 그냥 말씀 드린 것입니다..
어쨌든 이 메시지큐라는 것에 메시지가 저장되어 있다가 하나씩 하나씩 그에 대응하는 함수가
호출 되어지는 것입니다...
이 윈도우 메시지라는 것은 나중에 메시지 구조부분에서 정확하게 설명드리겠지만.. 단순히
WM_~식의 메시지 이름만 전달되는 것은 아닙니다...
예를 들어 마우스를 클릭하였다면.. 그때 커서위치가 어디였는지등의 추가정보가 같이 전달
됩니다.. 그러니 메시지 큐에는 이러한 내용들이 전부 들어 있겠죠..
메시지 핸들러 함수도 이러한 추가정보들을 처리하게 됩니다...
그럼 이 메시지큐에 있는 메시지를 어떻게 메시지 핸들러 함수와 연결시키는지 볼까요?
Visual C++ 6 강좌(029)
10.AFX(Application Frameworks) (계속7)
메시지와 메시지 핸들러 함수를 연결시킨다는 얘기는 이런 이런 메시지가 발생되었을 때
이런 이런 메시지 핸들러 함수를 호출시키는 루틴을 말하는 것이지요...
이는 switch,case문으로 쉽게 구현할 수 있습니다..
{
while(msg != WM_QUIT){
switch(msg){
case WM_CREAT:
(); break;
case WM_PAINT:
(); break;
....
}
}
뭐 이런식으로 하면 되지요.. 간단하지요? 여러분이 C의 기본문법정도는 알고 있다면, 이같은
문장은 쉽게 알 수 있습니다..그래도 간단히 설명하겠습니다..
while문으로 무한루프를 만들었군요.. 그야 당연한게 메시지란 놈은 프로그램이 시작해서 끝날 때
까지 계속 발생하는 것이니까요.. 프로그램의 종료를 의미하는 WM_QUIT메시지가 발생할 때 까지
아래의 switch, case 문을 계속적으로 실행한다는 말이군요..
msg라는 변수가 메시지의 이름을 받는데요.. 그 이름에 따라 실행되는 루틴이 달라지죠..
그래서 메시지가 WM_CREAT일 경우일 땐(즉, msg == WM_CREAT) ()함수를 호출하게 하였습
니다..이런식으로 모든 메시지에 대해 정의해 주면 프로그램실행동안 발생하는 모든 메시지에
대해 이를 처리하는 메시지 핸들러가 자동적으로 호출되겠지요...
이것을 우리가 해줘야 할까요? 벌써 다 되있으니 할 필요가 없습니다.. 우린 그저 그저..
어떤 메시지를 호출 하는 핸들러 함수가 뭔지만 알면 되고 그 안에 그 메시지가 발생하였을 때
해주어야 하는 일이나, 해주고 싶은 일을 넣어 주면 그만이죠...
그런데 이 메시지 핸들러 함수에 우리가 해야할 일을 넣어 준다는 것은 미리 정의 되어있는
CWnd클래스의 멤버함수인 메시지 핸들러 함수를 오버라이딩한다는 의미입니다..
이또한 당연하지요.. 이해가 가지 않으셨다면.. 앞부분의 함수의 오버라이딩부분을 완전히 이해
하지 못했다는 증거니 늦기 전에 한번더 보시고 꼭 이해하고 넘어가세요..
앞으로 우리가 MFC에 있는 클래스의 멤버함수를 사용하는 것은 대부분 오버라이딩해서 사용하게
될테니까요...뭐 상당수 그냥 호출만 해서 사용하는 경우도 많지만요...물론 클래스의 인스턴스
를 만들어서 사용해야지만요...
그럼 이쯤해서 메시지 큐에 저장되는 윈도우메시지가 어떻게 생겨먹었는지 볼까요?
3) 메시지의 구조
어떻게 생겼냐하면 구조체(struct)로 되어 있습니다...
typedef struct tagMSG{
HWND hwnd;
UINT message;
WPARAM wParam;
LPARAM lParam;
DWORD time;
POINT pt;
}MSG;
위와 같이 생겼다는 얘기죠.. 뭐 앞에 HWND니 UINT니 것은 모르더라도 변수이름만으로 대충
메시지에 어떤 내용이 들어가는지 충분히 짐작할 수 있습니다..
UINT형 message변수에는 메시지 이름 즉,WM_MOUSEMOVE와 같은 것이 저장된다는 말이죠..
그리고 메시지가 발생될 때 여러 추가정보가 같이 생긴다고 하였죠?
이런 추가정보를 wParam, lParam 변수가 가지는데요...만약 추가정보가 간단하다면
WPARAM(2바이트)형으로 선언된 wParam에 저장되고 좀 모자라면 LPARAM(4바이트)형으로 선언된
lParam변수에 저장하면 됩니다.. 그래도 안되면 어떻게 할까요.. 추가 정보가 너무 많아서
이 두 개의 변수로도 어쩔 수 없다면 말이죠...
그땐 포인터를 이용하면 되겠죠..만약 추가정보가 너무크면, 이에 필요한 메모리를 할당하여
이에 저장하고 그 포인터를 lParam에 넘기면 되지요.. wParam은 안되냐구요?
앞에서 윈도우 프로그래밍의 특징에서 32bit 프로그래밍을 설명하면서 4byte크기의 포인터를
사용한다고 하였죠...그래서 이 포인트를 이용하면 4기가바이트짜리 메모리까지 참조할 수 있다고
설명하였죠? 그러니 2byte인 WPARAM형으로 선언된 wParam변수에는 저장할 수 없죠...
이해 가시죠?
그리고 time에는 메시지가 발생한 시각이... pt에는 메시지가 발생하였을 때 커서의 위치를 저장
하는 변수입니다...
hwnd에는 참고적으로 메시지가 발생한 윈도우의 핸들이 저장되는데요.. 지금으로선 모르셔도
전혀 상관없습니다...
지금 제가 메시지 구조를 설명한 이유는 이러이러한 정보들이 메시지큐에 저장되고 이러한 정보를
이에 대응하는 메시지 핸들러 함수가 이용한다는 것이지....이를 달달 외워서 이에 대응하는
메시지 핸들러 함수에서 이렇게 저렇게 이용해야 된다는 얘기는 하고 싶은 건 절대 아닙니다..
그러니 아~~~~ 이런내용들이 메시지가 발생할 때 추가정보로 발생되고 이런정보들이 어떻게 저장
되든간에 메시지 큐에 저장되고...어떻게 이용하던간에 메시지 핸들러 함수가 이용한다는 거죠..
그걸로 100% 족합니다...
그리고 여기서 처음으로 생소한 데이터type이 나오는데.. 나중에 일괄적으로 설명하겠습니다..
한번 알고 나면 별거 아닌게.. 데이터 타입이고...쓰면 쓸수록 미묘한게 데이터 타입이니까요..
이제 메시지 구조까지 알아봤습니다.. 이제 뭘하면 좋을까요...
실제로 메시지 함수에 우리가 넣고자 하는 기능을 넣는 방법을 알아봐야겠군요...
Visual C++ 6 강좌(030)
10.AFX(Application Frameworks) (계속8)
4) 메시지 핸들러 함수 오버라이딩...
앞에서 간단하게나마 메시지 핸들러 함수에 우리가 하고자 하는 기능을 넣으려면..CWnd 의
멤버하무인 메시지 핸들러함수를 오버라이딩 해야 한다고 하였지요?
우리가 무슨멤버함수를 오버라이딩 할 때는 어떻게 하였죠?
예~ 일단 그 멤버함수가 있는 클래스를 상속받아 파생클래스를 만들고 거기에 오버라이딩하고
싶은 멤버함수를 다시 선언하지요? 그리고 클래스정의부분(멤버함수 정의부분)에 파생클래스에서
다시 선언된 기반클래스의 멤버함수를 재정의 하면 됩니다...
예를 들어 설명해 보겠습니다..
윈도우가 생성될 때 어떤 메시지가 발생하지요? 예 앞에 자주쓰이는 메시지 표를 보시면
WM_CREATE라는 메시지가 발생합니다...그러면 ()라는 메시지 핸들러 함수가 호출되겠죠?
그러면 이 ()라는 함수는 윈도우가 생성될 때 해야 할 여러 가지 기본적인 기능들을
수행합니다..
그랬으니 우리는 아무 내용도 입력하지 않았는데..로 만든 My.exe라는 파일을 실행시키
면 윈도우가 제대로 생성되지요?
이 ()라는 메시지 핸들러 함수는 CWnd클래스에 정의되어 있겠죠..
그런데 우리가 만들 프로그램에 어떤 윈도우가 생성될 때 무슨일을 추가해서 하고 싶다면..
어떻게 하면 되겠습니까? 함수의 오버라이딩을 사용하면 되겠군요...
먼저 CWnd클래스를 상속받아 C이란 파생클래스를 만들고 여기에 다시 를 다시 선언
하고 클래스 정의부분에(cpp) 다음과 같이 하면 됩니다...
int C::(LPCREATESTRUCT lp)
{
CWnd::(lp);
//우리가 추가하고자 하는 기능
}
이 새로정의하고자 하는 ()함수에 기반클래스인 CWnd의 멤버함수 함수를 호출
하고 그다음에 우리가 추가하고자 하는 기능을 넣어주면 되는 거죠....
지금 이설명이 이해되지 않는 분이 있다면...일단..옐로우카드입니다..
왜냐하면 앞으로 거의 MFC클래스를 위와 같은 함수의 오버라이딩을 이용하여 사용하게 될텐데..
이것이 이해가 되지 않으면 정말 큰일 이거든요...
이것이 이해가 되지 않으신 분들은 여기서 잠시 멈추시고..다시 앞으로 가서 클래스 설명부분을
처음부터 다시 보세요...그렇지 않으면 뒤의 설명은 보나 마나거든요..더 헷갈리게 될테니까요..
참고적으로 함수에 넘겨지는 인자 lp는 추가정보입니다..지금은 신경쓰지
마세요..
<가상함수(Virtual Function)>
여기서 저는 보통 함수의 오버라이딩과 메시지핸들러 함수의 오버라이딩의 차이점을 설명하기
위해 별로 설명하고 싶지 않던 얘기를 해야겠습니다..
원래 앞에서 클래스 설명부분에서 함수의 오버라이딩을 설명할 때 이를 설명해야 되는데요..
초반부터 좀 귀찮은 얘기를 하는 것 같아 피했는데..더 이상 피할 때가 없는 것 같습니다..
바로 가상함수(Virtual Function)에 대한 얘기를 하려고 하는데요..
가상(Virtual)이라는 말은 많이 들어보셨죠? 뭐 가상현실 같은 얘길 들어보셨을 테니까요..
가상(virtual)라는 말의 의미가 뭐죠? 가상현실이라고 하면 현실이 아닌데..현실같은거..
즉, 뻥인데..진짜같은거...뭐 그런거를 얘기할 때 가상이라는 말을 사용하지요?
가상함수도 마찮가지입니다.. 사실 존재하지 않는데..존재하는 것 같은 함수..뭐 그런 얘기죠..
존재하지 않는데.. 존재하는 것 같은 함수라...
정말 이해할 수 없는 얘기군요...가상함수는 함수선언부분앞에 virtual이란 키워드를 사용하여
클래스 선언부분에 선언합니다.
virtual void Virtual();
뭐 이런식으로 말이죠...
그럼 이런식으로 가상함수로 선언하면 어떻게 달라질까요?
이는 컴파일과 큰 관련이 있는데요...
우리가 함수를 만들어서 컴파일(compile)시키면, 각각의 코드(C++언어로 코딩한 소스등)가
기계어로 번역되겠죠? 그리고 끝입니까? 아니죠.. 이를 메모리에 저장할 거 아닙니까...
그리고 실제 이 함수가 호출되는 부분(함수를 사용한 부분)에는 이 함수가 저장되어 있는
메모리번지가 저장됩니다...
그래서 프로그램을 실행하면 차례차례 기계어로 번역된 명령어들을 수행하고 함수를 호출하는
부분을 만나면 그부분에 있는 메모리번지를 참조하여 실제 함수가 저장되어 있는 메모리번지로
점프(jump)해서 함수를 실행합니다.. 함수가 끝나면 전에 함수가 호출되었던 부분으로 다시
점프해서 되돌아 가겠죠...
여기까지 이해가 가시죠?
이렇게 함수를 호출하는 부분에 함수가 저장되어 있는 메모리 번지를 연결시켜주는 것을
함수의 바인딩이라고 합니다..
바인딩이라는 뜻이 영어로 '묶다' 뭐 대충 이런 뜻이잖아요..
그런데 이런 바인딩하는 방법이 두가지가 있다는 거죠...
정적바인딩(static binding), 동적바인딩(dynamic binding)!!
정적이라는 것은 움직이지 않는 것이라는 의미를 가지고 있죠? 그러니 뭔가 변하지 않는다는
의미가 있을겁니다...
그래서 정적바인딩은 컴파일을 할 때 바인딩(연결)할 수 있습니다..
컴파일할 때 바인딩 할 수 있다는 얘기는요..컴파일해서 기계어로 번역하고 실행파일 만들잖아요?
그때 함수를 호출하는 부분과 실제 그 함수가 저장되는 부분을 연결시킬 수 있다는 얘기죠..
그래서 컴파일할 때 함수를 호출하는 부분에 함수가 저장되어 있는 메모리 주소가 결정되어
버린다는 얘기죠..그러니 함수가 저장되는 메모리 주소가 변하지 않겠죠?
그래서 정적바인딩이라고 하는 겁니다...
그럼 동적바인딩은 컴파일 할 때 함수가 저장되어 있는 메모리 주소가 결정되지 않는다는 얘기
겠죠? 그냥 빈칸으로 두는거죠.... 그럼 언제 주소가 결정될까요?
프로그램실행시에 점프해야 할 메모리주소가 결정됩니다...
그러니 프로그램 실행할 때마다 점프해야 할 메모리 주소가 바뀌겠죠? 그러니 동적바인딩이라고
부르는 겁니다..
그런데 가상함수 얘기하다가 왜 동적바인딩이니 정적바인딩이니 하는 얘길 했을까요?
이는 virtual로 선언된 함수는 동적바인딩을 한다는 말입니다..
컴파일할 때 호출될 함수가 저장된 메모리번지가 정해지지 않고 프로그램 실행시에 결정된다는
얘기죠...그리고 virual로 선언되지 않은 보통 함수들은 전부 정적 바인딩을 합니다..
그럼 동적바인딩을 해서 뭐 어쩌겠다는 얘기죠/
왜 virtual이란 키워드를 써서 동적바인딩을 시킬까요?
게다가 동적바인딩을 하면 수행속도도 떨어지고(사실 요즘같은 시대에는 거의 차이가 없지만)
바인딩에 필요한 즉 메모리번지를 기억할 포인터가 필요하니 호출되는 함수당 4byte의 메모리
공간을 더 사용하는 셈이죠..
별로 좋을 것 같지 않는데.. 왜 동적바인딩을 시키는 거지요?
이부분에서 깊숙한 얘기를 해야되는데..그냥 생략하고 답만 간단히 말씀드리겠습니다..
지금으로썬 그리고 앞으로(?) 별로 알고 있지 않아도 상관없습니다..
답은 함수의 오버라이딩과 관련있습니다..
기반클래스의 A라는함수가 파생클래스에서 오버라이딩되었다고 합시다...
그러면 이 오버라이딩된 함수를 호출하면 기반클래스의 A라는 함수는 무시되고 파생클래스의
A라는 함수가 호출된다고 하였죠?
그런데 이를 장담할 수 없다는 얘기죠....(그 이유는 기회가 되면 말씀드리죠..속깊은얘기입니다)
그래서 파생클래스에서 오버라이딩된 A라는 함수가 제대로 호출되게 하려면 기반클래스의 A함수
가 가상함수, 즉 virtual로 선언되어야 한다는 것입니다..
그래야 동적바인딩을 시킬 수 있고 그래야 제대로 파생클래스의 오버라이딩된 A함수가 호출될
수 있습니다..
그런데 정말 어처구니가 없는 상황이 되었군요...
지금까진 속편하게 멤버함수를 만들어 이를 파생된 다른 클래스에서 오버라이딩해서 썼는데..
가상함수로 선언해서 정의해야 파생클래스에 오버라이딩을 제대로 할 수 있다니...
이것이 나중에 오버라이딩이 될지 않될지 어떻게 알 수 있단 말입니까?
제가 지금까지 써와던 수법과 지금 말하는 분위기로 봐선 이쯤에서 짜잔~하며 "이런 방법이 있습
니다"라고 얘기할 것 같지만 사실 없습니다..
기반클래스를 만들 때 이런 것까지 고려해서 조심스럽게 만들어야 한다는 거지요...
이 함수가 오버라이딩 될 가능성이 있는지를 고려해야 한다는 말입니다...
실제 MFC의 기본 클래스 즉, 프로그램을 만들 때 뼈대가 되는 클래스들의 상당부분이
가상함수로 되어있습니다.. 우린 이런 속사정도 모르고 신나게 오버라딩해서 쓸 생각만 했지요..
이제는 조금을 신경을 써야겠군요...
메시지 핸들러 함수의 오버라이딩에 대한 설명을 하다가 엄청나게 딴길로 셌군요...
하지만 꼭 그렇지만은 않습니다..
메시지 핸들러 함수는 가상함수로 선언되어 있지 않거든요...
그럼 분명히 메시지 핸들러 함수를 오버라이딩해서 쓸 수 있다고 쓰는 방법도 똑 같은 것처럼
예까지 들어 가며 설명해 놓고 이제와서 무슨 딴 소리냐구요?
물론 앞에 예에서처럼 보통함수와 같은 방법으로 오버라이딩하는 것까지 맞습니다...
전혀 틀린설명이 아닙니다..
가상함수의 오버라이딩은 메시지맵(message-map)이라는 방법을 사용합니다.
앞에서 C클래스 설명부분에 에 나와있는 영문장을 올려놓은 것을 보면
메시지맵 아키텍쳐(Message-Map Architecture)라는 말이 나오는데 바로 이 메시지 맵을 말하는
것입니다..
그럼 다음페이지에서는 그냥 CWnd클래스에서 모든 메시지 핸들러 함수를 가상함수로 선언해서
일반함수처럼 오버라이딩하면 될걸 왜 궂이 메시지맵이라는 요상한 방법을 사용하는가와...
메시지맵을 사용한 메시지 핸들러 함수의 오버라이딩 방법을 살표보겠습니다..
전에도 말씀드렸지만 메시지 핸들러 함수는 정말정말 기본이 되는 함수이고, 지겹게 쓰이는
함수이니 제대로 알아두셔야 합니다....그럼 넘어가죠...
Visual C++ 6 강좌(031)
10.AFX(Application Frameworks) (계속9)
4) 메시지 핸들러 함수 오버라이딩(계속)...
우리는 상속받은 기반클래스의 어떤 멤버함수를 파생클래스에서 오버라이딩하기 위해선
기반클래스의 그 멤버함수가 가상함수로 정의되어 있어야 한다는 것을 배웠습니다..
그런데 문제는 메시지 핸들러 함수가 CWnd에서 가상함수로 정의되어 있지 않다는데 있습니다..
왜 그랬을까요?
앞장에서 우리는 동적바인딩을 하기 위해서는 4바이트의 메모리 번지수를 저장할 공간이
더 필요하다는 것도 배웠습니다...
그럼 만약에 모든 메시지 핸들러 함수가 가상함수로 되어있어 동적바인딩을 하게 된다면
몇바이트의 메모리가 더 필요하게 되겠습니까?
그렇죠..CWnd클래스에 메시지 핸들러 함수가 200여개 있다고 했으니까..대충 800바이트가 더
필요한 셈이군요...뭐 800바이트 정도야 요즘같이 수십메가씩 하는 메모리에서는 껌일 수도
있지만요..
프로그램이 하나 실행된다고 하면 윈도우가 몇 개나 생깁니까?
기본적으로 프레임 윈도우가 있구요.. 뷰윈도우, 툴바, 상태바, 다이얼로그박스, 각종 컨트롤
까지 수개~수십개의 윈도우가 생기는데..이렇게 되면 조금 문제가 될 수도 있겠군요..
설령 문제가 되지 않더라도 이건 대단한 자원낭비입니다..
메모리라는 것은 컴퓨터 자원중에도 괭장히 중요한 시스템 자원이니까요..
게다가 이 호출되는 메시지 핸들러 함수가 저장되어 있는 메모리 주소를 가지게 됨으로써 필요한
메모리 공간이라는게.. 이 호출되는 함수의 구현과는 거의 무관하게 필요한 것이니...
이런 메모리 공간의 낭비의 문제 말고도 또 다른 문제가 있는데요...
우리가 기반클래스의 멤버함수를 가상함수로 정의했다는 것은 이를 나중에 파생클래스에서
오버라이딩할 수 있도록 배려한 것인데..
이 200여개나 되는 메시지 핸들러 함수가 전부 오버라이딩이 되냐 하면 거의 그렇지 않거든요..
각 위도우마다 한 2-5개 정도?? 어쨌든 정말 일부만이 오버라이딩이 되니...
이또한 쓸데없는 메모리 낭비 아닙니까
그래서 머리좋은 MS사 프로그래머들이 고안해 낸 것이 메시지 맵이라는 것입니다..
가상함수로 정의되지 않은 함수를 오버라이딩하였을 때 문제점이 뭐라고 하였죠?
이 오버라이딩을 한 함수를 호출하면 파생클래스의 오버라이딩된 멤버함수가 호출되어야 하는데..
기반클래스의 멤버함수가 호출될 수도 있다는 것이죠..
그러니 가상함수로 정의되어 있지 않은 메시지핸들러 함수를 오버라이딩 하면, 우리가 재정의한
메시지핸들러 함수(파생클래스의 멤버함수)가 호출된다는 보장이 없다는 거죠...
그런데 이 메시지맵이란 것을 사용하면 우리가 CWnd에서 상속받아 C란 파생클래스를 만들어
거기에 WM_MOUSEMOVE란 메시지가 발생할 때 호출되는 란 함수를 오버라이딩 하고
이 함수를 호출하면 CWnd(기반클래스)의 는 무시되고 파생클래스인 C의
함수가 호출된다는 얘기입니다..비록 이 함수가 가상함수가 아니더라도...
메시지 맵이란 일종의 매크로인데요..어쨌든 우리가 어떤 메시지 핸들러 함수를 오버라이딩하고
싶다면 메시지 맵에 일단 등록시켜놓고 우리가 배운 함수의 오버라이딩을 하면 된다는 거죠..
등록만 하면 됩니다.. 뭐 얘기는 길 게 해놓았지만..실제로 이를 이용하는 방법은 간단하기
그지없죠...
우리가 이 메시지맵에 오버라이딩된 메시지 핸들러 함수를 등록해 주면 이 등록된 함수들만
가상함수처럼 동적바인딩을 해줍니다..그래서 기반클래스의 메시지 핸들러 함수는 무시되고
우리가 재정의한 메시지 핸들러 함수가 호출되는 것이지요...
그럼 메시지 맵이 어떻게 생겼나 볼까요?
BEGIN_MESSAGE_MAP(C, C)
//{AFX_MSG_MAP(C)
ON_WM_MOUSEMOVE()
ON_WM_CREATE()
//{AFX_MSG_MAP
END_MESSAGE_MAP()
위의 모양처럼 생겼는데요..한번 훑어보니..두 개의 메시지 핸들러 함수가 등록되어 있는 것
같군요.. 이는 즉 이 두 메시지 핸들러 함수를 오버라이딩할테니.."오버라이딩된 파생클래스의
메시지 핸들러 함수를 호출해 주렴...어눌하게 기반클래스의 함수 호출하지 말고..."라는 뜻
입니다..
메시지 맵은 메시지맵의 시작을 나타내는 BEGIN_MESSAGE_MAP라는 매크로와 메시지의 끝을 나타
내는 END_MESSAGE_MAP이라는 매크로로 이루어져 있습니다.
그리고 BEGIN_MESSAGE_MAP매크로에 무슨 함수 인자처럼 들어간 두 개의 클래스는 뒤에 있는
클래스에서 상속받은 앞의 클래스의 메시지 맵이라는 의미입니다..
즉, C에서 상속받은 C의 메시지 맵이라는 말이죠..
이런 정보가 왜 필요할 까요?
우리는 메시지 맵을 이용하여 기반클래스의 메시지 핸들러 함수는 무시하고 파생클래스의 오버
라이딩된 메시지 핸들러 함수가 호출될 수 있도록 메시지 핸들러 함수를 쓰는 것이죠?
그래서 어떤게 기반클래스고 어떤게 파생클래스인지 메시지맵에게 알려줘야 합니다..
그래서 위와 같이 쓴 것이죠.. 물론 이것도 가 AFX의 파생클래스를 만들면서..
클래스마다 저런 골격을 만들어 놨습니다...
우리는 주석문 사이에 오버라이딩할 메시지 핸들러 함수명을 넣어주는 것으로 모든일이 끝납니다.
게다가 그것도 우리가 일일이 하는 경우는 거의 없고..클래스위저드를 이용하여 하지요..
클래스위저드를 이용하면 메시지핸들러 함수를 오버라이딩하기가 정말정말 쉽습니다..
그냥 어느 클래스에서 상속받은 어느 클래스의 메시지 핸들러 함수를 오버라이딩 하겠다..
라는 내용을 항목에서 찾아 클릭만 해주면...다 알아서 클래스선언부분에 선언도 해주고..
메시지 맵에도 등록시켜주고 함수 정의부분의 골격도 다 만들어 놓습니다..
우리는 골격이 다 만들어진 메시지 핸들러 함수의 정의부분에 우리가 넣어줄 기능만 적어주면
그걸로 끝이죠... 설명은 다소 어려웠을지 모르지만...실제 사용은 너무나 쉬어서 좀 민망하기
까지 하거든요...
어쨌든 제가 하고 싶었던 CWnd클래스에 대한 모든 설명을 마쳤습니다.
지금쯤 열심히 이글을 읽으신 분들은 지금까지 설명이 CWnd클래스에 대한 설명이었나?
하실분도 계실거예요..CWnd클래스라는 말은 별로 안나오고 순전히 메시지 핸들러 함수얘기만
했으니까요...
어쨌든 CWnd클래스는 메시지 핸들러 함수를 빼고 나면 시체나 다름없으니...설명은 다 한겁니다..
다시 한번 정리하죠..
CWnd클래스는 역시 C이라는 클래스의 파생클래스이고요..그 기능은 크게 두가지로..
하나는 윈도우 자체를 제어하는 것이고 다른 하나는 윈도우에서 발생하는 메시지를 처리하는
메시지 핸들러 함수부분이라는 것입니다..
그리고 메시지 핸들러 함수는 발생하는 윈도우 메시지에 대응하여 자동으로 호출되는 CWnd클래스
의 멤버함수입니다..
메시지는 메시지 이름과 추가정보,메시지가 발생한 시각, 커서의 위치등의 정보등과 함께..
메시지 큐(자료구조)라는 메모리공간에 저장되고 이를 하나씩 꺼내서 대응하는 핸들러 함수를
호출하게 되지요...
메시지와 핸들러 함수를 자동으로 연결시크는 것은 switch,case문으로 쉽게 만들 수 있고요..
메시지는 프로그램이 돌아가는 동안은 계속 발생하는 것이기 때문에 일종의 무한루프, 더 자세히
말하면 프로그램 종료를 나타내는 WM_QUIT라는 메시지가 발생할 때 까지 메시지가 발생할 때
이에 대응하는 핸들러 함수를 호출하게 하도록 하는 switch,case이 계속 실행되는 것입니다..
또한 메시지 핸들러 함수에 새로운 기능을 추가하거나 재정의 하기위해 함수의 오버라이딩을
사용하는데.. 이는 다른 일반함수와 마찮가지로..기반클래스에서 상속받아 오버라이딩하면
됩니다.. 그런데.. 메시지 함수는 가상함수가 아니므로..오버라이딩된 함수가 호출될 보장이
없습니다.. 그래서 메시지 맵이라는 일종의 매크로를 사용하여 동적바인딩을 시킵니다..
그래서 우리는 메시지맵에 오버라이딩한 메시지 핸들러 함수를 등록시키고 가상함수와 동일한
방법으로 오버라이딩을 하면 되지요...
이렇게 정리하니 몇줄 안되는데..너무 길 게 설명했다는 생각도 드는군요...
그럼 다음은 C클래스를 살펴보겠습니다...
Visual C++ 6 강좌(032)
10.AFX(Application Frameworks) (계속10)
3.C
앞에서 CWnd가 C와 CView클래스의 기반클래스라는 얘기를 하였지요?
그렇다면 C나 CView는 CWnd가 가지고 있는 기능을 다 가질 수 있겠군요...
물론 메시지 핸들러 함수를 사용한다면..파생클래스이니 메시지 맵을 사용하여 오버라이딩
했을테지요..실제로 보면 그렇게 되 있습니다...
어쨌든.. 다 알고 있다 하여도 다시한번 위치확인!!
CObject->C->CWnd->C
이젠 설명하지 않아도 다 아시겠지요.. 이 말이 무슨 의미 인지를...
그럼 이 클래스는 어떤 객체를 표현한거였지요? 예~~눈에 보이는 영역인 윈도우틀을 클래스로
구현한 것이 C지요? 이 설명은 너무 오래전에 해서..말씀드리기가 쑥스럽군요..
그렇다면 어떤일을 하겠습니까?
물론 윈도우틀과 관련된 모든 일을 수행합니다..
클라이언트 영역을 View로 덮어주는 일도 하구요..
메뉴와 툴바 상태바 같은 것도 생성시켜준다고요?
대단하시네요..물론 메뉴나 툴바, 상태바 같은 것은 대부분의 프레임윈도우가 수행하는 일이긴
하지만 프로그램에 따라서 달기도 하고 달지 않기도 하지 않나요?
그래서 이것을 위한 기능을 구현되어 있지 않고요..이러한 것들을 쉽게 달 수 있도록
하는 기능만을 가지고 있습니다..
그럼 툴바나,메뉴,상태바등을 프레임윈도우에 달려면 어떻게 하면 되겠습니까?
어허~~의견이 너무 제각각이군요..
앞으로 이와 유사한 질문을 제가 던지면 머리속에선 "어떻게"가 생각나면 안되고 "언제"가
먼저 생각나야 합니다..
왜냐하면 프로그램중에 언제 이러한 기능을 수행해야 할까를 생각하면 어디에다가 이 기능을
넣어야 하는지가 결정됩니다..앞에서 수도없이 말씀드렸잖아요...
그리고 나서야 비로서 "어떻게"게 나와야 합니다..
즉 "언제"->"어디에"->"어떻게"식으로 생각을 하시라는 얘깁니다...
그럼 이런식으로 생각해 봅시다...
언제 메뉴나 툴바나 상태바가 생기도록 하면 될까요? 프로그램 종료하기 직전에요?
프로그램 시작하자마자요? 아님 아무때나 꼴리는데로 하면 될까요?
예~~예~~슬슬 정답이 나오는군요...우리가 프로그램을 실행시키면 윈도우틀이(프레임윈도우)가
생기고 메뉴나 툴바등이 생기고 클라이언트영역이 허였게 되고(물론 아닌 것도 있고요)됩니다.
툴바나 메뉴등은 메인프레임에 다는 것이니까 윈도우가 생성될 때 달면 됩니다...
그럼 "언제"는 해결됐으니..."어디에" 이런 기능을 넣으면 되는지를 볼까요?
자주 쓰이는 메시지와 메시지 핸들러 함수를 표를 보셔서 아시겠지만..윈도우가 생성될 때
무슨 메시지가 발생한다고 하였죠? 예~~ WM_CREAT라는 메시지가 발생하죠?
그럼 이 메시지가 발생할 때 자동적으로 호출된느 메시지 핸들러 함수는 이지요?
그렇다면 답은 나왔네요..윈도우가 생성될 때 이런 메뉴나 툴바를 생성시켜야 하니까..
윈도우가 생성될 때 발생하는 메시지를 핸들링해주는 메시지 핸들러 함수 안에
메뉴나 툴바등을 생성시키는 루틴을 넣어 주면 되겠군요..
이제 "어디에"까지 해결됩습니다..
그런 다음에는 진짜 프로그래밍 실력발휘를 해야 하지요... 하지만 지금은 아니예요..
지금은 메뉴나 툴바를 생성시키는 시간이 아니거든요...
어쨌든 워크스페이스에서 ()함수를 찾아 클릭해 보세요...
C클래스를 찾아봐야 그런 클래스는 안보입니다...당연하지요..그건 MFC 이고..우리는
를 이용해서 이 클래스에서 상속받아 파생클래스를 이용하지요..
그 이름은? 예~~C입니다..이 항목을 클릭하시면..가 보이시죠?
그럼 어떻게 정의되어 있는지 한번 보세요...
보셨습니까? 뭐라고 알 수 없는 내용이 잔뜩있지요?
지금 자세히 살필 필요는 전혀 없지만 무슨 내용인지만 알고 넘어가자면
툴바클래스의 멤버함수인 Create라는 함수를 이용해서 툴바를 생성하였고..
상태바의 클래스의 멤버함수인 Create를 호출해서 상태바를 생성하였습니다.
마지막 부분에는 툴바가 도킹(메인프레임에 붙어있는 것)할 수 있도록 여러 가지 내용을 넣었군요
아무리 생각해봐도 정말 고마운 입니다..
친절하게도 이렇게 만들어 줬기 때문에 우리가 이를 컴파일해서 실행파일을 만들어 실행하면..
윈도우프레임에 툴바와 상태바등이 있는 거지요...
사실 이정도만 알고 있어도 상관은 없습니다.. C란 클래스는 다른 AFX클래스와 마찮가지로
처음부터 끝까지 여러분이 MFC Programming을 하는한 계속 나옵니다.. 벌써부터 힘빼지 맙시다..
지금은 그저 에게 감사의 마음만 가지고 있으면 됩니다..우리의 학습량을 어찌됐건..
줄여줬으니 까요...
마지막으로 워크스페이스를 다시한번 봅시다..C를 보고 그안에 어떤 멤버와 어떤 함수들이
있는지 살펴보십시오..그냥 눈인사 정도만 하세요..벌써부터 친해지려 하지 마시구요...
C클래스의 선언부분은 어떤 해더파일에 있지요? .h이지요?
그리고 클래스 정의부분은 .cpp에 있습니다..
그냥 눈요기로 대충 봐두세요...
Visual C++ 6 강좌(033)
10.AFX(Application Frameworks) (계속10)
4.CView
CView클래스도 CWnd클래스에서 상속받은 파생클래스입니다..그러니 메시지 핸들러 함수라든지
윈도우 자체를 제어하는 기능들을 다 포함하고 있는 셈이죠...
이 일반적인 원도우로서의 역할에다가 뷰윈도우로서의 고유한 역할을 추가한 것이 CView클래스
입니다..
벌써 설명을 다 했듯이 데이터를 여러 가지 방법으로 보여주는 역할을 하지요..
그리고 C나 C같은 클래스는 거의 대부분의 윈도우 응용프로그램들이 비슷비슷하게
가지고 있는 부분이지만 데이터를 처리하고 보여주는 등의 일을 하는 CView와 CDocument는
프로그램들마다 다르기 때문에 우리의 프로그래밍 실력은 여기서 판가름이 난다고 할 수 있죠..
C는 일관된 유저인터페이스와 관련이 깊기 때문에 다른 여타 프로그램들과 크게 다를
바가 없지만요 실제 우리가 이러이러한 기능을 하는 프로그램을 만든다 하면 그건 다른 프로그램
들과 다를 테니까 거의 대부분이 이 CView클래스와 CDocument클래스에 다가 이러한 내용을
넣어주기 때문에 실제 우리가 가장 많이 다루게 되고 가장 잘 알고 있어야 하는 부분입니다..
전에 설명했던 얘기를 간단히 요약해서 말씀드리자면,
우리가 이러이러한 기능을 하는 프로그램을 만든다고 하면 그러한 기능은 거의 CDocument에
넣게 되는데요..그 이유야 이 클래스가 데이터를 처리하고 저장하는 등의 일을 하기 때문이죠..
그런데..같은 데이터를 처리하였다 하더라도 이 데이터를 보여주는 방법은 정말 엄청나게 많거
든요?
예를 들어 우리가 증권투자를 도울 수 있는 주가분석프로그램을 만든다고 합시다..
그러면 주가와 관련된 데이터를 이러저러하게 처리하여 저장해야 되겠죠? 이렇게 해놓고..
이 처리된 결과를 뷰윈도우(클라이언트영역)에 보여줘야 하는데요..
만약 우리가 상장회사의 금일 주가변동을 시간대별로 보여줘야 한다면 어떻게 하면 될까요?
뭐 표를 만들어 가로축은 시간의 흐름을 세로축에는 상장회사별로 숫자로 보여 줄 수 도 있고요..
단순한 막대그래프 형태로 보여줄 수 있고요..아니면 곡선으로 보여줄 수 도 있고요..
고요...고요...고요...고요...정말 방법은 많겠죠?
데이터를 처리하고 저장하는 도큐먼트부분은 하나라도 보여주는 뷰부분은 여러개가 될 수 있겠죠?
그래서 뷰윈도우는 어찌됐던간에 어떤 도큐먼트와 연결되어있어야 합니다..
그렇지 않으면 이 뷰윈도가 어떤 도큐먼트에서 처리된 데이터를 보여주는 지 알길이 없으니까요..
그리고 도큐먼트의 입장에서 보면 하나의 도큐먼트가 여러개의 뷰를 가질 수 있는 셈이지요..
사실 지금으로선 이정도의 설명으로 CView클래스의 대한 설명은 끝입니다..
그러면 가 CView클래스에서 상속받아 만들어준 파생클래스 C를 한번보지요..
CView클래스가 선언된 부분이 저장되어 있는 헤더파일(.h)를 한번볼까요?
어떻게 선언되 있는지 보게요...어떻게 찾지요? 예 워크스페이스의 탭을 클릭해서
거기에 있는 Header Files 라는 폴더를 클릭해 주면 모든 헤더파일이 다 사이좋게 모여있죠?
거기에서 .h를 클릭하면 되죠..
지금까지 계속 이런식으로 파일을 찾았는데요.. 더 간단한 방법이 있습니다..
우리가 실제로 프로그램을 짤 때는 탭에 놓고 작업을 하는데요...
거기서 어떤 클래스의 헤더파일이나 소스파일을 보고 싶을 때 지금까지 한 방법으로 탭
으로 이동해서 찾아도 되지만요.좀 성가시죠...
더 쉽게 보려면 그냥 탭상에서 클래스 이름을 더블클릭하면 그 클래스가 선언되어 있는
헤더파일을 보여줍니다.. 간단하죠..
그리고 그 클래스의 어떤 멤버함수를 클릭하면 그 멤버함수가 정의되어 있는 부분을 보여주는데..
그 보여주는 부분은 곧 그 클래스의 멤버함수가 정의되어 있는 소스파일(cpp)중에 우리가 클릭한
멤버함수의 정의부분이 되는 셈이죠...
그럼 한번 의 C클래스를 더블클릭해 보세요... 좀전에 C에서 우리가
찾아서 클릭한 .h의 내용이 똑같이 나오죠...편리하군요...
그럼 진짜로 한번 훑어 보세요...
뭔소리를 하는건지...우리는 여기서 두가지 멤버함수만 기억해 두면 그걸로 족합니다..
뷰가 하는 역할이라는게..도큐먼트에서 처리되어 저장되어 있는 데이터를 가져다가 화면에 보여
주는거잖아요..그게 거의 전부죠..
그러면 앞에서 설명하였다시피...그렇다면 어떤 도큐먼트의 내용을 보여주자면..두 개의 두분으로
나뉘어 지겠군요..어떤 도큐먼트의 내용을 보여주는건지 일단 알아야 보여주던지 말던지..할테니
도큐먼트에 저장된 데이터를 가져오기 위해선 도큐먼트를 얻어와야 해야겠죠.. 그리고 얻어왔다면
보여주면 되는거죠..
그런데 보여주는 기능은 크게 두가지로 나눌 수 있습니다.. 너무나 당연한 얘기지만..
화면에 즉 모니터를 통해 직접 보여 줄 수도 있고...프린터를 해서 보여줄 수도 있잖아요?
그래서 화면에 보여주는 기능과 프린터 기능이 있겠지요..
그리고 어떤 내용을 어떻게 보여주느냐하는 문제는 순전히 프로그래머 맘데로입니다.
너무나 당연한 얘기지요..
이런이런 내용을 이렇게 보여주라는 법은 없으니까요...이 부분에서 여러분의 아이디어와
프로그래밍 실력이 결합해서 정말 쥑이는 프로그램이 나올 수 있는 거죠...
또 기름끼 좌악 빼서 보면 다음과 같이 되어 있습니다...
Class C: public CView
{
public:
C* ();
Virtual void (CDC* pDC);
protected;
virtual BOOL (C* pInfo);
virtual void (CDC* pDC, C* pInfo);
virtual void (CDC* pDC, C* pInfo);
};
정말 기름끼 쫙 빼군요...
맨위의 문장은 C가 AFX클래스인 CView에서 public으로 상속받은 파생클래스라는 얘기구요..
다 아시죠?
그 다음 public로 선언되어 있는 함수들은 외부사용이 가능한 멤버함수라는 말이구요..
그 아래 protected로 선언되어 있는 함수들은 이 클래스의 내부적으로나 파생클래스 즉, 우리가
만든(사실은 가 만들어준) C를 상속받을 파생클래스(현재로선 없지요..)내부에서
사용가능하지만 외부사용은 안된다는 의미이지요..
위에서 설명했던 도큐먼트를 얻어오는 역할을 함수가 있군요...
선언된 모양을 보니 도큐먼트의 더정확히 말하면 도큐먼트 클래스의 포인터를 얻어오는군요..
즉 C이라는 클래스형의 포인터를 반환한다는 말이죠.. 우리는 이 포인터를 가지고 도큐먼트의
데이터를 이렇게 보여줄 수 있습니다..
그리고 실제로 보여주는 기능을 함수인 라는 멤버함수가 보입니다..
실제 이 멤버함수가 CView클래스의 대부분의 역할을 담당한다고 볼 수 있습니다..
우리는 이 함수안에다 실제로 보여주는 기능을 넣어 주면 되는 거죠..
이렇게 다 만들어져 있으니 정말 편리하기 그지 없습니다..
어떻게 정의되어 있는지 볼까요..
워크스페이스에 C클래스를 보면 함수가 있지요?
클릭해 보세요.. 어떻게 되어 있습니까?
C::(CDC* pDC)
{
C* pDoc = ();
ASSERT_VALID(pDoc);
//TODO: add draw code for native data here
}
음~~역시 예상한데로 별내용 없군요..
일단 를 호출했군요..
C형으로 선언된 pDoc이라는 포인터에 가 반환하는 현재 도큐먼트의 포인터를
저장하죠.. 그럼 우리는 이 pDoc이라는 포인터를 이용하여 도큐먼트의 저장된 데이터를 참조
할 수 있는겁니다..
주석문에 //TODO라는 부분에다가 우린 실제로 어떤 데이터를 어떻게 보여주는지를 넣어주어야
겠지요..이걸로 끝~~~
클래스 선언부분으로 돌아와서 함수밑에 보니 On어쩌구Printing이라는 함수가 세 개
있군요...Printing이라는 말을 보니 프린터 출력과 관계된 함수인거 같죠?
이 프린터를 출력하는 부분은 뒤에서 설명하도록 하구요...
우리가 CView라는 클래스를 보면서 기억해야 할 부분은 3가지 정도입니다..
이것만 기억해 주고 넘어가면 되는 거죠..
있다.
관련함수를 이용하여 보여 준다..
이것으로 CView클래스의 내용은 마치구요.. 다음은 CDocument클래슬 살펴보죠...
Visual C++ 6 강좌(034)
10.AFX(Application Frameworks) (계속12)
5.CDocument
이제 AFX클래스의 마지막인 CDocument클래스를 살펴보도록 하겠습니다..
CDocument클래스는 CView클래스와 함께 우리의 프로그래밍 실력을 마음껏 발휘하는 곳이죠..
특히 CDocument는 실제 데이터를 처리 저장하는 기능을 하기 때문에 우리가 이러이러한 일을 하는
프로그램을 만들겠다고 마음먹었다면 이러이러한 일이라는 것이 전부 CDocument가 해야 하는 일
이 됩니다..가 CDocument클래스의 파생클래스인 C이라는 클래스를 만들어 놓았지만..
안에보면 별내용이 없기 때문에 우리가 실제로 다 해줘야 하지요..
CDocument는 MFC 계층도에서 다음과 같은 위치에 있습니다...
CObject -> C -> CDocument
CView와 C가 C에서 상속받은 CWnd의 파생클래스임에 반에 CDocument는 C
에서 직접 상속받았군요...
그러니 현재로선 CWnd의 멤버함수는 자유롭게 사용할 수 없습니다...
뭐 어쨌든 간에 중요한 건 이런거죠...
CDocument라는 클래스는 눈에보이지는 않지만 실제 프로그램이 하는 기능을 하는 정의하는 곳입니다
MFC에 CDocument클래스에는 다음과 같은 내용이 정의되어 있습니다.
대충 이런 기능을 가지고 있지요.. 그러니 우리가 만든(실제 가 상속받아 만들어준) 클래스
인 C도 위와 같은 내용을 가지고 있지요..
파일로부터 데이터를 읽거나 저장하는 기능이란 파일메뉴 같은데 보면 '열기','저장'이라는 서브메뉴
가 하는 기능을 말하죠...
우리가 윈95용 프로그램에서 파일열기나 저장하기를 클릭하면 다이얼로그 박스가 나오는데요..
그 생김새가 다 똑같죠? 이건 일관된 인터페이스와 관련이 깊은 것인데요...
일반적으로 윈도우 응용프로그램들은 파일열기, 저장하기, 글(폰트나 폰트사이즈,색깔등등),색깔
선택(미리정의된 색깔이나 사용자 정의 색을 선택)등의 기능을 하는 대화상자(Dialog box)가
거의 똑같습니다..
MFC도 위와 같은 공통된 대화상자를 제공하거든요...그냥 갖다가 조금만 수정해서 사용하면 되는
거죠...이런걸 흔히 공통 다이얼로그 박스라고 합니다....
파일 열기,저장 얘기하다가 조금 얘기가 삐져나왔는데요..
어쨌든 지금으로썬 CDocument를 이해한다는 것은 별 의미가 없구요..
단지 CDocument에 우리가 실제로 프로그램이 이러이러한 기능을 수행할 수 있도록 그 기능을 넣어
주어야 한다는 것과 실제 우리가 프로그래밍을 할 때 제일 중요시 여겨야 한다는 거죠..
보통의 비주얼 C++책을 보면 인터페이스(뷰만해도 리스트뷰,트리뷰등, 각종 컨트롤 뭐~ 버튼,스핀버튼
어쩌구 저쩌구..분할윈도우..어쩌구 저쩌구..)에 너무 많은 내용을 두고 설명을 하는데요..
사실 프로그램을 짜다 보면 이런 인터페이스는 일부만 사용하게 됩니다..
물론 엄청나게 큰 프로그램을 만든다고 치면 전부 사용할 지도 모르지만 그런 프로그램을 어느누가
혼자서 만들겠습니까?
제가 말씀드리고 싶은 것은 이런 것에 너무치중해서 공부하다보면..공부를 다하고 나서도 아무 프로
그램도 만들지 못한다는 거죠...
인터페이스란 것은 사용자들이 쉽게 능률적으로 프로그램을 사용할 수 있도록 도와 주는 것에 불과
하구요..실제 프로그램에서 중요한건 그 프로그램이 어떤 일을 어떻게 하느냐입니다..
이 어떤 일을 어떻게 하느냐는 CDocument클래스와 관련이 깊지요...완전히 객체지향적인 프로그램을
만든다면요...
물론 인터페이스가 중요한건 사실이고..요즘 프로그램은 인터페이스에서 판가름이 나는 경우도 비일
비재합니다.. 엄청나게 중요한 인터페이스라 할지라도 프로그래밍을 공부하는 초급.중급자들에게는
인터페이스보단 프로그램의 기능쪽에 더 많은 관심을 가지라는 말씀입니다..
예를 들어 우리가 초고속 검색 DB를 만들겠다고 마음먹었다고 합시다..
검색DB의 인터페이스란게..너무 뻔한거 아닙니까? 이런저런 단어를 입력하면 그 단어와 관련된
데이터를 보여주죠.. 이런 프로그램에서 혁신적인 인터페이스란게 어디있겠습니까?
그런걸 만든다면 여러분은 돈방석에 앉으실 준비를 해야죠...
"머리속으로 단어를 생각하면서 프로그램을 째려보면 그와 관련된 데이터가 나온다"
모르긴 몰라도 뭐 이정도 인터페이스는 되야 될겁니다..
실제 이런 프로그램에선 어떤 것이 중요하겠습니까? 검색엔진!! 이 검색엔진을 어떻게 만드느냐죠..
실제 DB는 어떠한 자료구조를 가지고 효율적으로 저장되며 검색되어지는가..어떠한 방식으로 검색
하는가..검색속도를 얼마만큼 개선시킬 수 있는가..즉 응답시간(리스폰스 타임-response time)을
얼마나 줄일 수 있는가? 입력된 단어와 데이터의 연관성은 어떠한 식으로 결정하는가? 등등
실제로 중요한 질문이 엄청나게 많습니다...
이런 곳에 더 많은 관심을 가지셔야 한다는 말이죠...상대적으로 쉬운 언어들이 엄청나게 많습니다.
흔히들 4GL(4세대 언어)라고 하는 언어들이죠.. 뭐 델파이니..영~~파이니(경상도 사투리)하는거
말입니다.. 이런 언어들은 어떻게 보면 결정적인 한계를 가지고 있습니다...델파이로 DB를 개발한다고
하면 흔히 델파이에서 제공되는 검색 알고리즘을 사용합니다..ACCESS같은 거도 마찮가지구요..
그럼 DB를 만드는 것은 쉬울 수 있지만..뭔가 혁신적인 검색엔진을 가진 DB는 가질 수 없게 되죠..
한마디로 그놈이 다 그놈이라는 말이죠...아주 지나치게 심하게 얘기해서 말이죠...
제가 말씀드리고자 하는 내용은 뭐 C++가 최고다.. 델파이는 영~~파이다 라는 얘기가 아닙니다..
델파이를 쓰는 사람이 얼마나 많다고요...델파이는 VC++이 가지지 못한 수많은 장점들을 가지고 있
습니다..
여러분이 C언어 프로그래머가 되고자 하신다면...C언어의 특징을 이해 하신 셈이죠...
그 많은 언어중에 C를 선택하셨으니까요...
그 특징을 보건데...C언어는 요즘같이 일관된 인터페이스의 프로그램을 만드는 세상에서 인터페이스
설계조차 다른 언어들에 비해 그리 편한 것만은 아니라는 거죠..
실제 ACCESS로 DB를 만든다고 하면 버튼 하나를 만들어 이것이 하는 일을 정의하는 과정이 VC++보다는
훨씬 간단합니다.. 그럼에도 불구하고 여러분이 C++을 하는 이유는 그런 4GL로 만든 DB보다 더 강력
한 DB를 만들 수 있기 때문이죠...
저도 이 강좌를 앞으로 진행하면서 인터페이스와 관련된 내용은 그렇게 심도있게 하지 않을 것입니다.
이런 인터페이스는 프로그램을 만들면서 필요해 질 때 봐도 된다는 거죠..
컨트롤 중에 진행컨트롤이라는 것이 있는데요.. 우리가 파일같은거 복사할 때나 프로그램 로딩할 때
흔히 나오는 거죠..통신에서 다운로드받을 때도 흔히 볼 수 있는거죠..지금 어디까지 다운을 받았다
하는 것을 비쥬얼하게 보여주는 컨트롤인데요...
지금 그런걸 우리가 X빠지게 배워서 뭐 어쩌겠다는 겁니까?
그런걸 배우는 시간에 파일을 어떻게 저장하고 어떻게 읽으며 이러이러한 데이터를 어떻게 처리해야
하는 지를 배우는게 남는거라는 거죠...
그런다음에 해도 전혀, 네버, 절대로 늦지 않는다는 얘기죠..
CDocument는 완전히 잔소리만 하다가 볼일 다 봤습니다..
실제로 볼 내용도 없어요.. 실제 우리가 프로그램을 짜면서 보고 느껴야 하는 거죠...
한가지만 기억하고 넘어갑시다..CView에서 처리된 데이터를 보여주죠?
보여줄 데이터를 처리하는 일을 하는 객체가 도큐먼트구요..이 도큐먼트를 클래스로 구현한 것이
CDocument라는 거죠...
이것으로 AFX(C,C,CView,CDocument)설명을 마치도록 하겠습니다..
별로 한 것도 없이 뭔갈 마친다고 하니까 좀 쑥스럽군요...
다음으론 우리의 첫 번째 프로그램 을 만들어 보도록 하겠습니다..
실제 우리가 배운 AFX클래스와 간단한 그래픽관련 클래스와 멤버함수를 이용하여 만들 예제 프로그램
입니다..
지금까지 한번도 VC++을 직접 해보지 않으신 분들은 다음장에서 설명하는 예제는 꼭 따라서 해주시길
바랍니다..직접 해보지 않고 생각치 않으면 아무짝에도 쓸모없는 설명이 되 버리니까요..
Visual C++ 6 강좌(035)
11.AFX 예제 프로그램 - .exe
드디어 프로그램이라는 걸 만들어 보는군요.. 그것도 Visual C++로 말이죠...
실제로 프로그램을 개발하는 것 처럼 설명을 진행해 나가도록 하겠습니다...
우리는 지금 이런 프로그램을 개발하려고 합니다..
우리가 클라이언트 영역에 클릭을 하면 조그만한 사각형이 그려집니다..
거의 점만한 사각형으로 우리는 그림을 그릴 수 있습니다.. 물론 아주 불편하게 말이죠...
또한 그림을 확대에서 그릴 수도 있게 끔 할 겁니다....
이정도도 대단하죠?
그럼 프로그램 설계를 해보죠..
기능부터 정의를 할까요? 만들 프로그램에 대해서 설명하면서 다 해 버리긴 했지만..실제 프로그램을
만든다는 가정하에 해봅시다..
끝이죠? 이만하면 됬습니다..
복잡한 프로그램은 그 기능을 명확히 정의하는게 그리 간단하지 않습니다..
프로그램의 기능을 명확하게 정의해 놓으면 프로그램을 실제 짤 때 상당히 도움을 받습니다..
그럼 프로그램의 기능을 정의했으니 그다음은 어떻게 하죠?
객체지향적 프로그램을 만들거니까
'WindowsPrograming' 카테고리의 다른 글
MFC 팁...! (0) | 2007.03.11 |
---|---|
[MFC로 구현하는 DB 프로그래밍] ② DB의 다리 ODBC (0) | 2007.03.11 |
API/MFC차이점 (0) | 2007.03.11 |