Programming hwp
|
|
- 윤조 이
- 7 years ago
- Views:
Transcription
1 프로그래밍개론 2 강의자료 - i -
2 1 장 C 언어의개선판으로서의 C++ 언어 1.1 C++ 언어의특성 C++ 언어는 C 언어의수퍼셋이다. 즉, C의모든기능을포함하고있으며, 따라서 C++ 컴파일러는 C 프로그램도컴파일할수있다. 다만이에대한예외는 C++ 언어에는 C 언어에는없는키워드들이있으며, 이들을변수나함수이름으로사용하는 C 프로그램은 C++ 컴파일러에의해오류로취급될것이다. C 언어의설계에가장큰영향을미친언어는 BCPL이며, BCPL에가장큰영향을미친언어는 Algol68로알려져있다. 이를요약하면 A(lgol68) ==> B(CPL) ==> C와같이알파벳순으로진행된다. C++ 언어는 C에클래스와몇가지기능을추가하고개선하여만들어졌으며, D로시작하는이름을가질수도있었을것이다. 그러나 C++ 언어를설계한 AT&T 벨연구소의 Bjarne Stroustrup는 C++ 라는이름을택함으로써 C++ 언어가새로운언어이기보다는 C 언어를확장한것임을나타내었다. Bjarne Stroustrup은 C+ 라는이름을선택하지않은것은이것이 C 언어에서구문오류이기때문이라는우스갯소리를남겼다. C++ 언어는데이터캡슐화 (data encapsulation), 상속 (inheritance), 오버로딩 (overloading), 다형성 (polymorphism) 등의객체지향 (object-oriented) 프로그래밍기법들을지원한다. 이러한개념들에대해서는추후의장들에서차차살펴볼것이다. 1.2 C++ 예제프로그램 #include <iostream> using namespace std; void main() { int inch; cout << "inches="; cin >> inch; cout << inch; cout << " in = "; cout << inch*2.54; cout << " cm n"; cout << inch << " in = " << inch*2.54 << " cm n"; 위의프로그램은인치단위의정수값을읽어 cm 단위로환산하여출력하는프로그램이다
3 1.3 Visual Studio 2008/2010 에서의프로그램작성과실행 여기에서는 Visual Studio 2010을기준으로설명하고있지만, Visual Studio 2008의사용법도이와거의동일하다. Visual C++ 에서는프로그램을프로젝트라고부르며, 프로그램작성의시작은프로젝트유형의선택에서시작된다. 텍스트기반의프로그램을작성할때는 Win32 콘솔응용프로그램이라는프로젝트유형을선택한다. Visual Studio의 [ 파일 ] 메뉴의 [ 새로만들기 ] - [ 프로젝트 ] 라는메뉴항목을선택하면아래와같은화면이나타난다. (Visual Studio 2010의시작페이지화면상의 < 새프로젝트 > 항목을클릭해도된다.) 이때왼쪽열에서 <Visual C++> 의 <Win32> 라는템플릿을선택하고가운데열에서프로젝트유형으로 <Win32 콘솔응용프로그램 > 을선택한다. 그리고하단에서이름표제옆의사각형안에프로젝트이름을적당히입력한다. 위의예제의경우 Inches라는이름을사용할수도있다. < 위치 > 표제아래의사각형에는프로젝트파일들이들어갈폴더를위한경로를표시하는데, 오른쪽의 < 찾아보기 > 버튼을눌러탐색기기능을사용하여폴더를선택할수도있다. 이화면의나머지사항들은원래대로두고, < 확인 > 버튼을누른다
4 < 다음 > 버튼을눌러진행하면아래의화면을만나게되는데, 응용프로그램의 < 추가옵션 > 으로 < 빈프로젝트 > 를선택하고 < 마침 > 버튼을누른다. C++ 소스파일을작성하기위해아래화면에서마우스커서를솔루션탐색기안의 < 소스파일 > 옆에위치시킨후마우스오른쪽버튼을클릭하여나타나는팝업메뉴에서 [ 추가 ] - [ 새항목 ] 메뉴항목을선택한다
5 아래화면에서파일종류를 <C++ 파일 (.cpp)> 로선택하고, < 이름 > 표제옆의사각형안에파일이름을적당히입력한다. 이경우파일의확장자는.cpp로지정된다. 파일이름은프로젝트이름과같아도무방하며, 여기에서는 Inch라는이름을사용하고있다. 위의화면에서 < 추가 > 버튼을누르면다음과같이화면의오른쪽에편집창이만들어지며, 이안에 C++ 프로그램을입력한다
6 소스프로그램을입력한후 [ 디버그 ] 메뉴 [ 디버깅하지않고시작 ] 메뉴항목을선택하면, 컴파일작업과실행이동시에이루어진다. 실행화면의모습은옆의그림과같다. [ 파일 ] 메뉴의 [ 솔루션닫기 ] 메뉴항목을선택함으로써프로젝트를닫는다. 다음에다시이프로젝트를불러들일때는 [ 파일 ] 메뉴의 [ 열기 ] - [ 프로젝트 / 솔루션 ] 메뉴항목을선택한다음 Inches.sln 파일을찾아서열어준다. 물론윈도우탐색기에서이파일을찾아열어도된다. 1.4 주석 C++ 는 "/*" 로시작하여 "*/" 로끝나는 C 스타일의주석형식도지원하며, "//" 기호를사용하여표시되는한줄짜리주석형식도지원한다. 다음예를참고하라. // get_it function reads in input values void get_it() { // do something. "//" 기호이후그줄끝까지주석으로간주된다. 1.5 입출력 C와 C++ 사이의중요차이점중의하나가표준입출력방식이며, C에서의 <stdio.h> 파일에의해규정된라이브러리가 C++ 에서 <iostream> 파일에의해규정되는라이브러리로대체되었다. 물론 C의모든기능을지원하는 C++ 언어프로그래밍에서여전히 C의 stdio 라이브러리를사용할수있지만, 이는권장되지않는다 터미널입출력 터미널이란대형컴퓨터를여러사용자가함께사용할수있도록개별사용자에게지급되는장비로서모니터와키보드만으로구성되어대형컴퓨터에시리얼 (serial) 케이블로연결 - 5 -
7 되는장비이다. 여기서터미널 I/O란표준입출력장치인키보드와모니터화면에대한입출력을나타낸다. iostream 라이브러리는 C++ 언어의여러가지객체지향적특성을잘활용하고있다. (C++ 프로그램에서정의되는객체형식에대해 iostream 라이브러리의입출력방식을적용할수있도록확장할수있다.) 기본적입출력에 iostream 라이브러리를이용하는것은복잡하지않다. 아래의두가지표현은동등하며, 동일한결과를출력한다. cout << "hello n"; cout << "hello" << endl; cout은 stdio 라이브러리의 stdout에해당되는출력스트림객체이며, << 연산자 (insertion operator) 가출력될데이터를받아오기위해사용된다. 사전정의된표준출력객체에는오류메시지를위한 cerr과 clog가있는데, cerr에서는버퍼가사용되지않고 clog에서는버퍼가사용된다. 정수, 실수, 문자, 문자열을가리키는포인터등의표준타입들은위의방식을사용하여출력될수있다. 여러항목의출력은한줄로표현될수도있고여러줄에나누어표현될수도있다. 다음코드를살펴보자. int i = 2; float f = 3.14; char c = 'A'; char *s = "hello"; cout << s << c << f << i << endl; 위의코드는다음과같은내용을출력한다. ( 상수 'A' 는 C에서는 int 타입이지만 C++ 에서는 char 타입이다.) helloa3.142 다음코드도동일한내용을출력한다. cout << s << c; cout << f; cout << i << endl; 출력형식조정을위한기법으로항목들사이에공백이나탭을나타내는스트링을추가할수있다
8 int i = 2; float f = 3.14; char c = 'A'; char *s = "hello"; cout << s << " " << c << " t" << f << " t" << i << endl; 출력스트림에끼워넣어포맷조정에사용되는 manipulator라고불리는기호들이있다. ( 일부시스템에서는이들기호를사용하기위해 <iomanip> 파일을 include 시켜야한다.) dec 10진법정수표현 oct 8진법정수표현 hex 16진법정수표현 endl 라인종료 ends 스트링종료 (' 0') flush 출력버퍼비우기 (flush) setw(w) 최소출력폭을 w로지정 ( 기본값은 0) setfill(c) 채움 (fill) 문자를 c로지정 ( 기본값은공백 ) setprecision(p) 실수유효숫자자릿수를 p로지정 다음코드의출력결과는그림과같다. cout << "[" << setw (6) << setfill('*') << 192; cout << "]" << endl; cout << hex << "[" << setw (6); cout << setfill('*') << 192 << "]" << endl; cout << setprecision(4) << << endl; 키보드로부터의입력도입력스트림객체 cin과 >> 연산자 (extraction operator) 를사용하여유사한방식으로표현된다. 아래와같은코드는세개의정수를읽어들인다음차례로 i, j, k에저장하게된다. int i, j, k; cin >> i >> j >> k; 문자배열에스트링을읽어들일경우에는하나의단어를읽는다. ( 단어들은공백으로분리된다.) - 7 -
9 cout 에의해출력가능한모든표준타입값은 cin 을통해입력가능하다. cin 스트림을포함하는식은 EOF를만나면그값이 0이되며, 따라서아래와같은 while 루프로파일끝까지문자데이터를읽어처리할수있다. ( 이경우 whitespace는건너뛰고읽게됨을유의하라.) char c; while (cin >> c) cout << c; 파일입출력 텍스트파일에대한입출력은 "fstream" 파일을추가로 include 시키고 ifstream 타입또는 ofstream 타입변수를선언함으로써표준입출력의경우와유사하게처리될수있다. 다음프로그램은 "xxx.txt" 라는이름의파일에서읽은문자들을 "yyy.txt" 라는이름의파일에쓰는일을파일끝까지반복한다. ( 아래예제에서파일객체생성에문제가발생할경우, 해당파일객체변수의값은 0이된다.) [ 참고 : Visual Studio에서이프로그램을실행시킬경우파일들의위치는프로젝트폴더가된다. 명령창에서실행시킬경우파일들의위치는프로그램을실행시키는폴더가된다.] #include <iostream> #include <fstream> using namespace std; void main() { char c; ifstream infile("xxx.txt"); ofstream outfile("yyy.txt"); if (outfile && infile) // They will be 0 on error. while (infile >> c) outfile << c; - 8 -
10 위의코드는파일을정확히복사하지는못하는데, 이는문자에대한 >> 연산자가공백, 탭, ' n' 등의 whitespace를무시하기때문이다. whitespace까지정확히복사하려면아래와같이 get() 함수를사용하면된다. ( 이절에서이용하고있는객체, 객체의생성, 객체에적용되는 get() 이나 close() 함수등에대한설명은클래스개념을다루는 2장으로미룬다.) while (infile.get(c)) outfile << c; 입출력파일에대한 close() 함수는꼭필요하지는않다. 파일변수를포함하는코드블록이끝나면해당변수도사라지게되며, 이때파일닫기도자동으로이루어진다. 그러나블록종료전에파일에대한닫기가필요할경우, 아래와같이 close() 함수를사용할수있다. outfile.close(); 스트링입출력 메모리상의스트링으로부터읽을수도있고, 또출력결과를메모리상의스트링으로보낼수도있다. 이는 stdio 라이브러리안의 sscanf() 함수나 sprintf() 함수의기능에해당된다. 이를위해 "strstream" 파일을 include 시키고입력스트링 (istrstream) 변수또는출력스트링 (ostrstream) 변수를선언해야한다. 다음코드를살펴보자. char s[100]; ostrstream outstring(s, 100); outstring << 3.14 << " is pi" << ends; cout << s; 위의코드에의해문자배열 s는 "3.14 is pi" 라는텍스트로채워지고이는다시화면에출력된다. 이때 ends가사용되지않았다면스트링이제대로종료되지않았을것이다. 만일 s의크기를초과하는데이터가주어질경우, outstring이알아서출력을멈추도록되어있다. 이미값을갖고있는스트링 s에서읽어들이고자할경우에는다음과같은코드를사용할수있다. char *s = " cat"; istrstream instring(s, strlen(s)); float f; int i; char t[100]; instring >> f >> i >> t; - 9 -
11 1.6 변수선언위치 C++ 프로그램에서는변수선언은아래예와같이임의의위치에나타날수있으며, C에서와같이블록앞부분에명령문들이나타나기전에선언되어야한다는제약은없다. { int a, b;... code... int j;... code... int k = func(a, b);... code... for (int i = 0; i < 10; i++) const C++ 에서초기화되어값이변할수없는변수를정의할때는 const라는키워드를변수선언앞에쓴다. 이기능은현재 C 언어에도반영되어있다. #define에의해유사한심볼을정의할수있지만, 타입규칙의적용이가능하다는점에서 const의사용이권장된다. 아래와같은선언이있으면, MAX에대한값의변경시도는컴파일러가오류로처리할것이다. const int MAX = 100; 포인터의경우에는의미가좀더복잡해지며, 아래예를참고하라. const char *p; char const *q; char * const r; // *p = 'A' 는오류, p = q는허용됨 // 위의경우와같음 // *r = 'A' 는허용됨, r = q는오류 스트링을다루는많은함수에서입력스트링의경우 const char * 타입의파라미터를많이사용한다. 아래의 strncpy() 함수의한예이다. /* Visual Studio 도움말 : The strncpy function copies the initial count characters of strsource to strdest and returns strdest. If count is less than or equal to the length of strsource, a null character is not appended automatically to the copied string. If count is greater than the length of strsource, the destination string is padded with null characters up to length count. */
12 char *strncpy(char *strdest, const char *strsource, int count) { char *p = strdest; while (count-- > 0 && (*p++ = *strsource++)!= ' 0') ; return strdest; QUIZ (1) strsource 에전달되는주소값이 번지, strdest 에전달되는주소값이 번지, 번지에들어있는데이터가 'A', 'B', 'C', ' 0' 라고가정하자. count 에전달되는값이 4 인경우에대해 strncpy() 함수가종료하는시점 ( 즉, return 문에도달하는시점 ) 에 p, strsource, strdest 등포인터의값, count 의값, 그리고 번지에서시작되는메모리에저장되는값등을구하라. (2) count 에전달되는값이 3 인경우와 5 인경우에대해위의문제에답하라. (3) while 문을아래와같이변경할경우 (1) 의답은어떻게달라지는지구하고, 또이러한변경이함수의기능에미치는영향을설명하라. while (count-- > 0 && *p!= ' 0') *p = *strsource, p++, strsource--; (4) while 문을아래와같이변경할경우함수의기능에미치는영향을설명하라. while ((*p++ = *strsource++)!= ' 0' && count-- > 0) ; (5) 위의 strncpy 구현이 Visual Studio 도움말과기능상차이나는부분을찾아라. 그리고그러한차이를해소하기위해함수를적절히수정하라. 1.8 함수오버로딩 (overloading; 중복정의 ) 동일한함수이름에파라미터의개수나타입을달리하여여러개의함수를정의하고구별하는함수오버로딩은 C 언어에는없는기능이다. #include <iostream> using namespace std; #include <string.h> int max(int x, int y) { return x > y? x : y;
13 int max(int x, int y, int z) { return max(x, max(y, z)); char const *max(char const *x, char const *y) { return strcmp(x, y) > 0? x : y; void main() { int a = max(3, 5); cout << max(a, -a, 10) << endl; cout << max("abc", "de") << endl; 1.9 디폴트인수 (default argument) C++ 에서는함수호출시파라미터에대응되는인수가생략될경우의기본값, 즉, 디폴트인수를정할수있도록허용하고있다. 다음예를살펴보자. #include <iostream> using namespace std; void sample(char *s, int i = 5) { cout << "parameter 1 = " << s << endl; cout << "parameter 2 = " << i << endl; int main() { sample("test1", 10); sample("test1"); return 0; 첫번째호출에서는파라미터 i에전달되는값은 10이며, 두번째호출에서는 i에대응되는실인수가없으므로파라미터 i의값은 5인채로수행된다 메모리할당 C에서는메모리할당과해제를위해 malloc(), free() 등의함수를사용하였지만 C++ 에서는사용이보다간편한 new 및 delete 연산자로대체하고있다. 아래코드를살펴보자. 메모리의할당은 new type 형식으로표현되며, type이나타내는객체를저장할수있는공간을확보하고그주소를전달한다. delete 연산자는 new로할당된메모리블록을해제한다
14 int *p; p = new int; // p = (int *) malloc(sizeof(int)); (in C) *p = 12; cout << *p; delete p; // free(p); (in C) 배열을나타내는메모리블록의할당을위해서는 new type[size] 형식을사용할수있으며, 이경우해제시에는 delete [] 연산자를사용해야한다. 다음예를참고하라. int *p; p = new int[100]; // p = (int *) malloc(sizeof(int) * 100); (in C) p[10] = 12; cout << p[10]; delete [] p; // free(p); (in C) 위의예에서배열크기는상수 100 대신임의의수식으로표현될수도있다. new와 delete에서는기본데이터타입뿐만아니라사용자가정의한구조체등에대해서도메모리할당을할수있으며, 표현방법에있어기존타입의경우와차이가없다. 아래예를참고하라. struct node { int node ; data; *next; void main() { node *p; // C++ 에서는 node 앞에 struct 필요없음 p = new node; p->data = 10; delete p; 1.11 참조선언 (reference declaration) C에서는호출함수에서정의된변수가피호출함수안에서변경되도록하기위해해당변수의주소를나타내는포인터를전달하는방식을사용해야하는데, 두변수의값을교환하기위한아래의 swap() 함수가한예이다
15 void swap(int *px, int *py) { int t; t = *px, *px = *py, *py = t; void main() { int a = 10, b = 5; swap(&a, &b); cout << a << " t" << b << endl; C++ 에서도그와같은방식을사용할수도있지만, 구문을보다간결하게만들기위해참조선언을제공하고있다. 아래의코드에서 swap(a, b) 로호출될경우파라미터 x와 y는실인수 a, b를나타낸다. 즉, a, b의값을전달받는것이아니라 x와 y가각기 a와 b의별명처럼작용한다. 따라서 x와 y의값을변경시키면바로그순간 a와 b의값이변경되는것이다. 이러한파라미터전달방식을 call by reference 방식이라고한다. void swap(int& x, int& y) { int t; t = x, x = y, y = t; void main() { int a = 10, b = 5; swap(a, b); cout << a << " t" << b << endl; int& 타입으로선언된 x 와 y 는정수에대한참조역할을한다. 지역변수도참조변수로선언될수있으며, 이경우반드시초기화를통해참조하고자하는변수를표시해야한다. 아래의예에서참조변수 b는참조대상변수 a와동일한주소를나타내며, 변수 a에대한별명의역할을한다. int a; int& b = a; a = 0; b = 5; cout << a << endl; 이코드의출력결과는 5 인데, 이는 b 와 a 가동일한메모리객체를나타내기때문이다
16 스택 (stack) 예제 1 스택은여러개의값을저장하되, 접근방식이 LIFO(Last-In First-Out) 순서를지원하는데이터구조이다. (Playing with C 13장또는 The C Programming Language 4장참고 ) 스택에데이터를저장하는작업을 push, 스택에서데이터를꺼내는작업을 pop이라고부른다. 스택은흔히배열을사용하여구현되며, 일련의 push는값들을배열의앞에서부터차례대로채워나갈것이다. 예를들면, push 3, push 7, push 4를차례대로수행한후의스택의모습은다음과같다 (top = 3) 이상태에서 pop을수행한후의스택의모습은아래와같으며, pop을통해 4를얻게된다. 3 7 (top = 2) 스택의구현을위해흔히스택의상단, 즉, push나 pop이이루어지는위치를나타내는인덱스를사용하는데, 이인덱스는흔히 top이라고불린다. 구현에따라 top은마지막데이터의위치를나타내거나혹은그다음빈칸을가리킨다. 우리는 top이마지막데이터다음빈칸을가리키는방식을채택하기로한다. 아래의예제코드에서는스택의저장을위해고정크기의배열을사용하는대신 new 연산자를이용하여동적으로생성하고, 이를위해 create라는함수를둔다. 따라서이러한스택은 push나 pop 등을통해사용하기전에반드시 create() 를먼저호출해주어야한다. #include <iostream> using namespace std; struct Stack { double *store; intcapacity; int top; ; int create(stack &s, int size = 100) { s.capacity = size; s.top = 0; return (s.store = new double[size])!= 0; // If the stack is full, then push val onto the stack s and return 1, // otherwise simply return 0 to indicate a failure of the push operation int push(stack &s, double val) { if (s.top < s.capacity) { s.store[s.top++] = val; return 1; else return 0;
17 double pop(stack &s) { if (s.top > 0) return s.store[--s.top]; else { cerr << "Error: pop from an empty stack" << endl; return 0; void display(stack &s) { cout << "Stack Contents = ( " for (int i = 0; i < s.top; i++) cout << s.store[i] << ((i < s.top - 1)? " " : " )"); cout <<", top = " << s.top << endl; void main() { Stack s; create(s); push(s, 2.0); push(s, 11.1); push(s, 5.5); display(s); cout << "popped value = " << pop(s) << endl; display(s); push(s, 7.0); display(s); < 실행결과 > Stack Contents = ( ), top = 3 popped value = 5.5 Stack Contents = ( ), top = 2 Stack Contents = ( ), top = 3 Stack 파라미터가참조방식으로전달되고있음에유의하자. 이유는물론스택에변경이일어나야하기때문이다. display() 함수는스택의현재상태를보여주기위한함수이다. 이함수의경우스택에변경이일어나지않으므로 Stack 파라미터가참조방식으로전달될필요는없다. 연습문제 ( 모든입출력은 iostream 라이브러리를사용해서처리한다 ) 1. 아래두선언의차이를설명하라. char * const s; const char * t;
18 2. 아래의 cout 명령들의정확한실행결과를주어진표안에기입하라. int m = 123; double x = (1) cout << setw (5) << m << m; (2) cout << m << setw (5) << m; (3) cout << setprecision (2) << x << setw (7) << setprecision (3) << x; (4) cout << setprecision (4) << setw (8) << x; 3. 다음프로그램을읽고, 아래질문에답하라. void swap(int& x, int& y) { x = x + y, y = x - y, x = x - y; void main() { int a = 10, b = 5; swap(a, b); cout << "a = " << a << ", b = " << b << endl; swap(a, a); cout << "a = " << a << endl; (1) 프로그램실행결과를예상하여적어라. (2) 프로그램을실제로컴파일하여실행한결과를쓰고, 그와같은결과가나오는이유를설명하라. 4. 섭씨온도를나타내는 float 타입의값을읽은다음화씨온도로변환하여출력하는프로그램을작성하라. 다음출력예를참고하라 degrees Celsius converts to degrees Fahrenheit
19 5. 두개의정수를읽어들인다음앞의값이클경우, 같을경우, 작을경우등에대해각각 감소, 변화없음, 증가 라고출력한다. 6. 센티미터단위의길이를나타내는실수값을읽은다음피트와인치단위로변환하여출력하라. 피트단위값은정수이며, 인치단위값은소수점아래한자리까지만표시한다. 1 인치는 2.54cm, 1 피트는 12 인치이다. 다음출력예를참고하라 centimeters is 10 feet 11.2 inches. 7. 초단위의정수값을읽은다음시 / 분 / 초로환산하여출력하라. 다음출력예를참고하라 seconds is equivalent to 2 hours 2 minutes 2 seconds. 8. 입력으로두개의정수가주어지는데, 이들의의미는다음과같다. 첫번째정수는 24시체계로작업시작시간을나타낸다. 예를들면, 14:45는오후 2시 45분을나타낸다. 두번째정수는작업지속시간을나타낸다. 예를들면, 345는 3시간 45분, 10023은 100시간 23분을나타낸다. 프로그램은위의숫자들을읽고작업종료시간을 24시체계로표시하는데, 작업종료시점이작업시작시간과같은날이아닐경우괄호안에며칠후인지를표시한다. 다음출력예들을참고하라. Start time is 1415 Duration is 50 End time is 1505 Start time is 2300 Duration is 200 End time is 100 (+1 day) Start time is 2300 Duration is 2610 End time is 110 (+2 days) 모든분단위값은 60 미만이어야하며, 시작시간은 24 미만이라야한다. 이를어긴입력에대해서는적절한오류메시지를출력하라
20 년 1월 1일을기준으로표시된날짜에대해몇년몇월몇일 ( 요일 ) 인지계산하여출력하라. 1970년 1월 1일은 0으로나타낸다. 월 / 요일등은아래예에서와같이이름표시를사용한다. 입력 : 0 출력 : January 1, 1970 (Thursday) 입력 : 출력 : May 19, 1997 (Monday) 입력 : 출력 : October 4, 2024 (Friday) 10. 일 / 월 / 연도를나타내는 3 개의정수를읽고그다음날짜를출력하라. 다음입 / 출력예를참고하라. 입력 : 출력 : Date following 28:02:1992 is 29:02: 가나타날때까지숫자들을읽은다음그합을출력하라. (-999는합계산에포함되지않음 ) 개의숫자를읽고평균, 최대값, 최소값등을출력하라. ( 배열사용금지 ) 13. EOF 조건이발생할때까지숫자들을읽고평균, 최대값, 최소값등을출력하라. ( 배열사용금지 ) 14. 진법을나타내는숫자와해당진법으로표시된숫자를읽은다음 10진법으로변환하여출력하라. 진법을나타내는숫자는 2 이상 36 이하이어야한다. 입력 출력 ========== ====== (8진법으로 77) (3진법으로 1111)
21 15. EOF가나타날때까지임의의텍스트를읽은다음각모음의개수와자음전체의개수를세어출력하라. 출력예 : Numbers of characters: a 3 ; e 2 ; i 0 ; o 1 ; u 0 ; consonants EOF가나타날때까지영문텍스트를읽으면서한줄에한단어씩출력하라. 영문알파벳이외의모든문자는출력에나타나지않아야한다. 출력예 : Read a file of English text and print it out etc. 17. 양의정수 n에대한 Euler 수 E(n) 은 n 미만의숫자들중 n과서로소관계에있는숫자들의개수를나타내며, E(n) = { i 0 <i<n and GCD(i,n) = 1 와같이정의될수있다. 몇개의예를들어보면아래표와같다. n E(n) EOF 를만날때까지숫자들을읽고이숫자들에대한 Euler 수를계산하여출력하라. HINT 두정수 m과 n의최대공약수 GCD(Greatest Common Divisor) 를계산하는 Euclid 알고리즘은다음코드로표현될수있다. [n = 609, m = 105에대해테스트해보라.] while (n > 0) { r = m m = n; n = r; /* GCD(m,n) is in m */
22 18. 파일의끝을만날때까지계속정수들을읽고, 그중인접한두값의차중가장작은값을출력하는프로그램을작성하라. 입력값의개수나입력정수값의범위에제한을두어서는안된다. [ 예 : 입력 , 답 : 8 (1과 9의차 )] 19. 아래박스의내용은간단한덧셈연습프로그램의실행결과이며, 내용중밑줄부분은사용자가입력한데이터를나타낸다. 프로그램은두자리숫자두개를임의로생성하여덧셈문제로제시하고답을입력받아검사하며, 매번문제를푼후사용자에게계속여부를물어 'Y' 라고대답하면다시반복하는프로그램이다. 이와같은프로그램을작성하라. ( 난수생성을위해서는 <stdlib.h> 의 rand() 함수를사용한다. 반복구조를위해서는 do-while 루프를사용하라.) < 실행결과 > = 78 Correct 계속하려면 Y, 아니면아무키나 : Y = 94 Correct 계속하려면 Y, 아니면아무키나 : Y = 333 Wrong 계속하려면 Y, 아니면아무키나 : n Press any key to continue 20. 두개의정수 m, n이주어질경우이들에대한최대공약수 gcd(m,n) 를계산하는 Euclid 알고리즘은다음과같이재귀적으로정의될수있다. gcd(m,n) = m if n = 0 gcd(n, m mod n) if n > 0. 위의정의를사용하여 m과 n의최대공약수를계산하는재귀적 C 함수 int gcd(int m, int n) 를작성하라. 21. 지수승 x n 을계산하는아래함수 power() 는 O(n) 시간을소요한다. double power(double x, int n) { double p; for (p = 1; n > 0; n--) p *= x; return p;
23 power(x,n) 은아래재귀적절차를사용하면훨씬효율적으로계산될수있다. ("n div 2" 는정수나눗셈을의미함 ) n 이짝수 x n = (x n div 2 ) 2 n 이홀수 x n = x(x n div 2 ) 2 위의재귀적정의를사용하여 O(log n) 시간을소요하는 power(x,n) 함수를작성하라 진수값 n과진법을나타내는정수 b를파라미터로받아서 n을 b진법으로출력하는함수 base() 를작성하라. (1 < b < 10) 예를들면, base(10,3) 는 101을출력할것이다. 23. 스트링을입력받은다음그안의문자들을역순으로출력하라 x8 체스보드상에 8 개의퀸들을어느두개도동일한대각선, 행, 열에놓이지않도록배치하는방법은모두몇가지인지계산하라. 25. 임의의길이를갖는이진스트링을읽어 16진법으로변환하여출력하라. ( 입력스트링의길이는 1000을넘지않는다고가정한다.) 예 : ===> 2F152F152F 양의정수를변수 n에읽어들인다. 1, 2,..., n의모든순열을출력하되순열이나타내는숫자가오름차순이되도록한다. 입력 : 3 출력 : 두개의정수 m, n (m < n) 을읽고집합 {1, 2,..., n 의숫자들에대한길이 m인모든선택순열을출력하되순열이나타내는숫자가오름차순이되도록한다. 입력 : 2 3 출력 :
24 28. 이름을나타내는문자열과세개의숫자로이루어진입력데이터를파일에서읽고, 입력데이터와입력데이터의숫자의합을구하여출력하는 C 프로그램을작성하라. 파일끝을만날때까지입력데이터를반복하여처리한다. 입력데이터는예는다음과같다. Tom Steve 출력결과는다음예와같은형식을따른다. Tom Steve 주어진단어와문장을읽은다음그단어가문장에서몇번나타나는지검사하는프로그램을작성하라. 첫번째줄에는단어가주어지며, 두번째줄에문장이주어지는것으로가정한다. 출력예 : The word is "the". The sentence is "the cat sat on the mat". The word occurs 2 times. 30. (a) 1000 이하의모든소수들을출력하되아래의 Sieve 알고리즘을사용하라. 목적은 number[] 라는배열이 i가소수일경우에 number[i] == PRIME이되도록만드는것이다. 처음에는 0과 1 이외의모든숫자를소수로표시한다. 그런다음남아있는소수들중가장작은숫자부터차례로선택하여그배수들은소수가아닌것으로표시한다. 이모든반복후에 number[] 배열이나타내는대로소수들을출력한다. (b) 위의프로그램을다음사항들을충족하도록개선하라. 범위를나타내는값 n을 1000으로고정하는대신입력받는다. sqrt(n) 이하의소수들에대해서만배수들을찾는작업을수행한다
25 31. EOF 를만날때까지다음작업을반복하는프로그램을작성하라. 연 / 월 / 일을의미하는 "yyyy mm dd" 형식의세정수를읽고다음형식으로출력한다. ( 입력은 " " 으로가정 ) September 20, 1992 (Sunday) 달이름과요일이름들을나타내는 char 포인터배열을사용하라. 32. 아래의변환표를이용하여배열 s[] 안의문자들을변환한결과를배열 t[] 에저장하는함수 code(char s[], char t[]) 를작성하라. from: abcdhijlmnoprstuwxyz to: CDEFABUWJKLMQRSTHIOP s[] 안의문자들중변환표의 "from" 부분에나타나지않는문자들은원래대로둔다. 변환표는함수 code[] 안에둔다. ( 예 : s[] = "Yes, I am." then t[] = "YeR, I CJ.") 33. (a) 복소수를적절히표현할수있는구조체 Complex 를정의하라. (b) 첫번째파라미터로 '+', '-', '*', '/' 중의하나를받아적절한계산결과를리턴하는함수 Complex complex_arith(char operator, Complex a, Complex b) 를작성하라. 34. 파일을사용하는프로그램을아래지시사항에따라작성하라. (a) 메모장프로그램을사용하여다음과같은내용의텍스트파일을만들어 test.dat라는이름으로저장하라 (b) 위에서작성한 test.dat 파일을읽고 1000보다큰숫자들을모두 bignumbers.txt라는파일에저장하는 C++ 프로그램을작성하라. bignumbers.txt에서한행에한개의숫자만나타나야한다
26 2 장클래스 2.1 struct 안에함수 C++ 에서는 struct 정의안에함수도포함될수있으며, 이와같이 struct 안에선언되는함수를멤버함수라고부른다. 그리고 struct 안의데이터들은필드, 데이터멤버, 멤버변수등으로부른다. 아래의코드는이름 (name), 주소 (address), 나이 (age) 등을데이터멤버로포함하는 struct 를보여주고있다. 이 struct 정의안에는또함수 print() 와 setage() 등도포함되어있다. 즉, C++ structure는데이터의표현뿐만아니라그와같은데이터에적용할작업을나타내는함수도함께포함할수있다. struct person { char name [80], address [80]; int age; void print (void); void setage (int n) { age = n; ; 일반적으로멤버함수들은 struct 밖에서별도로정의된다. struct 밖에서멤버함수를정의할때는아래예에서와같이함수이름앞에 struct 이름과범위지정연산자 (::, scope operator) 를덧붙인다. void person::print () { cout << "Name: " << name << endl << "Address: " << address << endl; 위에정의된 struct는다음코드에서와같이이용될수있다. ( 아래예에서 p는데이터속성과함수속성을함께가지며, 이렇게구성되는프로그램요소를객체 (object) 라고부른다. 즉, p는하나의객체이다.) person p; strcpy(p.name, "Karel"); strcpy(p.address, "Rietveldlaan 37"); p.setage(45); p.print(); 일부멤버함수의경우에는 struct 정의안에함수정의가포함될수도있는데, 대체로이러한함수들은위의 setage() 함수처럼짧고간단하다. 이러한정의는 inline 정의라고불린
27 다. 함수가정의되는위치는 struct 밖이라도아래코드와같이앞에 inline이라는키워드를사용하면 inline 정의가되며, 이는전역함수의경우에도적용된다. inline void person::setage(int n) { age = n; inline int max(int x, int y) { return x > y? x : y; 일반함수는별도의위치에코드가생성되고, 함수호출은파라미터전달, 함수로제어이동, 함수실행, 결과반환및호출위치로의복귀등으로이어진다. 이러한수행방식은함수안에서이루어지는작업이간단할경우실제계산보다부대작업에더많은시간을낭비할수도있다. inline 함수는함수계산코드를함수호출위치에만들어넣음으로써보다효율적인프로그램을만들기위한함수정의방식이다. 함수호출위치가여러곳이고함수코드가길경우에는 inline 함수의사용이전체실행파일을길게만드는단점이있다. 또재귀법 (recursion) 을포함하는함수정의도 inline 방식에는적합하지않다. 프로그래머가 inline으로함수정의를표현하는것은컴파일러에게제안하는것이며, 함수를 inline 함수로취급할것인지에대한최종결정은컴파일러가한다. C 스타일의구조체로구현된 Date - 구조체와이를지원하는함수들사이의관계가불명확하다. struct Date { int d, m, y; ; void init_date(date& d, int, int, int); void add_year(date& d, int n); void add_month(date& d, int n); void add_day(date& d, int n); C++ 스타일로구현된 Date - 구조체와이를지원하는함수들사이의관계가명확하다. struct Date { int d, m, y; ; void init(int dd, int mm, int yy); void add_year(int n); void add_month(int n); void add_day(int n); void Date::init(int dd, int mm, int yy) { d = dd, m = mm, y = yy;
28 ... Date my_birthday; void f() { Date today; today.init(8, 9, 2008); my_birthday.init(6, 10, 1956); Date tomorrow = today; tomorrow.add_day(1); Shop 예제 1 & 2 몇몇종류의상품을취급하는가게에서의판매처리를보여주는예제이다. 고객이선택한상품들의코드를입력한다. 입력된각상품코드에대해상품코드, 상품이름, 가격등을출력하되, 잘못된상품코드에대해서는적절히오류메시지를출력한다. 상품코드대신 'quit' 라고입력하면, 금액합계를출력하고프로그램을종료한다. MerchandiseTable 이라는구조체를통해다음과같은표를구현한다. code name price m01 soda 1000 m02 icecream 700 m04 chocolate 2000 "" MerchandiseTable에서위와같은내용의표를생성하는 init() 함수, code 값에대해이름을알려주는 getname() 함수, code 값에대해가격을알려주는 getprice() 함수등을정의한다. #include <iostream> using namespace std; #include <string.h> struct CatalogEntry { char code[10]; char name[20]; int price; ;
29 struct MerchandiseTable { CatalogEntry tab[10]; ; void init(); char const *getname(char const *code); int getprice(char const *code); void MerchandiseTable::init() { strcpy(tab[0].code, "m01"); strcpy(tab[0].name, "soda"); tab[0].price = 1000; strcpy(tab[1].code, "m02"); strcpy(tab[1].name, "icecream"); tab[1].price = 700; strcpy(tab[2].code, "m04"); strcpy(tab[2].name, "chocolate"); tab[2].price = 2000; strcpy(tab[3].code, ""); char const * MerchandiseTable::getname(char const *code) { for (int i = 0; strlen(tab[i].code)!= 0; i++) if (_stricmp(tab[i].code, code) == 0) return tab[i].name; return 0; int MerchandiseTable::getprice(char const *code) { for (int i = 0; strlen(tab[i].code)!= 0; i++) if (_stricmp(tab[i].code, code) == 0) return tab[i].price; return 0; void main() { MerchandiseTable mtab; char code[10]; char const *name; int price; int total = 0; input the merchandise code (or 'quit'): wrong code input the merchandise code (or 'quit'): m01 soda 1000 input the merchandise code (or 'quit'): m02 icecream 700 input the merchandise code (or 'quit'): wrong code input the merchandise code (or 'quit'): wrong code input the merchandise code (or 'quit'): m04 chocolate 2000 input the merchandise code (or 'quit'): m02 icecream 700 input the merchandise code (or 'quit'): mtab.init(); total amount =
30 while (1) { cout << "input the merchandise code (or 'quit'): "; cin >> code; if (_stricmp(code, "quit") ==0) break; if (name = mtab.getname(code)) { total += price = mtab.getprice(code); cout << code << ' t' << name << ' t' << price << endl; else cout << "wrong code" << endl; cout << endl << "total amount = " << total << endl; 다음코드는위의 Shop 예제에서상품정보가 catalog.txt 라는파일에들어있는것으로가정하여프로그램을수정한것이다. 이는 MerchandiseTable 클래스의 init() 함수에반영되어있다. 이프로그램은또한판매물품정보를끝에모아서영수증형태와유사하게출력하는데, 이를위해 ostrstream 객체를통한스트링출력을이용한다. ( 수정된부분은적색으로표시 ) Merchandise Table m01 soda 1000 m02 icecream 700 m04 chocolate 2000 #include <iostream> #include <fstream> #include <strstream> using namespace std; #include <string.h> struct CatalogEntry { char code[10]; char name[20]; int price; ; struct MerchandiseTable { CatalogEntry tab[10]; input the merchandise code (or 'quit'): m01 input the merchandise code (or 'quit'): m02 input the merchandise code (or 'quit'): m03 wrong code input the merchandise code (or 'quit'): m04 input the merchandise code (or 'quit'): quit RECEIPT m01 soda 1000 m02 icecream 700 m04 chocolate 2000 total amount = 3700 ; void init(char const * filename); char const *getname(char const *code); int getprice(char const *code);
31 void MerchandiseTable::init(char const * filename) { ifstream file(filename); int i; for (i = 0; file >> tab[i].code; i++) file >> tab[i].name >> tab[i].price; tab[i].code[0] = ' 0'; <catalog.txt> 파일 m01 soda 1000 m02 icecream 700 m04 chocolate 2000 cout << "Merchandise Table" << endl << endl; for (i = 0; tab[i].code[0]!= ' 0'; i++) cout << tab[i].code << ' t' << tab[i].name << ' t' << tab[i].price << endl; cout << endl; char const * MerchandiseTable::getname(char const *code) { for (int i = 0; strlen(tab[i].code)!= 0; i++) if (_stricmp(tab[i].code, code) == 0) return tab[i].name; return 0; int MerchandiseTable::getprice(char const *code) { for (int i = 0; strlen(tab[i].code)!= 0; i++) if (_stricmp(tab[i].code, code) == 0) return tab[i].price; return 0; void main() { MerchandiseTable mtab; char code[10]; char const *name; int price; int total = 0; char receipt[1000]; ostrstream strout(receipt, 1000); mtab.init("catalog.txt"); while (1) { cout << "input the merchandise code (or 'quit'): "; cin >> code; if (_stricmp(code, "quit") ==0) break; if (name = mtab.getname(code)) { total += price = mtab.getprice(code); strout << code << ' t' << name << ' t' << price << endl; else cerr << "wrong code" << endl;
32 strout << ends; cout << endl << "RECEIPT" << endl << endl << receipt; cout << endl << "total amount = " << total << endl; 2.2 정보은닉 (data hiding) 과클래스 일반적으로클래스에서정의되는데이터멤버에대해해당클래스를이용하는프로그램에서직접접근할필요는없으며, 그러한접근은바람직하지않을수있다. 예를들면, 일련의데이터항목리스트를포함하는객체가있으며그리스트가배열로구현되어있다고하자. 그러한객체안의리스트에새로운항목을추가하기위해직접그배열에접근할수있을경우, 원래클래스설계자의의도에어긋나는리스트가만들어질수있다. 예를들어, 데이터항목에포함된어떤속성값들의합을클래스안의데이터멤버를통해표현하고있는데, 그와같은사항은모르면서배열에데이터항목만추가함으로써객체의일관성을해칠수도있는것이다. 이러한접근을방지하기위한기법이정보은닉이며, 데이터멤버에대한직접적인접근은금지하면서데이터를이용하기위해클래스안에정의되어있는멤버함수를이용하도록한다. 정보은닉은 C++ 나자바와같은객체지향언어들에서일반적으로지원되는프로그래밍개념의하나이다. 정보은닉의또다른효과는클래스에서은닉되어있는부분을변경하더라도클래스를이용하는프로그램에영향을미치지않는다는것이다. 예를들어원래는고정된크기의배열로구현되어있었지만, 수용할수있는데이터항목수에유연성을두기위해포인터로선언한후실행중에크기를지정할수도있고보다복잡한데이터형식을사용할수도있다. 물론이러한데이터멤버구현에대한변경은클래스멤버함수구현도함께변경하게될것이다. 그러나이러한변경은클래스내부에국한되며, 일반적으로해당클래스를이용하는다른부분에는영향을미치지않는다. 정보은닉대상은데이터멤버에국한되지는않으며, 클래스밖에서호출될필요가없는멤버함수도해당된다. 이러한함수는클래스내부에서만사용될것이다. C++ 에는정보은닉과관련하여사용되는키워드로가시성 (visibility) 수식어 private, public, protected 등이있다. 이러한키워드들은 struct 정의안에나타날수있다. "public:" 이나타나면그이후의 struct 필드나멤버함수들은어디에서나접근가능하다. "private:" 이나타나면그이후의 struct 필드나멤버함수들은그 struct 안에서만접근할수있다. 즉, 그 struct의멤버함수에서만접근가능하다. struct의멤버함수나데이터멤버는별도지정이없을경우 public 가시성을갖는다. class의멤버함수나데이터멤버는별도지정이없을경우 private 가시성을갖는다. C++ 에서 class와 struct는기본적으로같다. 유일한차이는바로위에기술된디폴트가시성이다. ( 이후클래스에대한설명은 class와 struct에공히적용된다.)
33 protected 가시성을갖는멤버들은현재의클래스와현재클래스를상속받아정의되는모든클래스에서접근가능하다. (2.7절상속참고 ) 일반적으로데이터멤버들은 private 가시성을갖게되며, 클래스외부에서호출하여사용될멤버함수들은 public 가시성을갖게된다. 아래코드에서는가시성표현을포함하여 person 클래스를정의하고있다. ( 아래코드가 class이든 struct이든아무차이가없음을유의하라.) class person { private: char name [80], address [80]; public: char const *getname (void); char const *getaddress (void); void setname (char const *n); void setaddress (char const *a); void print (void); ; 이경우아래의마지막문장에서사용되는 x.name 은가시성에위배되는오류이다. person x; x.setname("frank"); strcpy(x.name, "Knarf"); // ok, setname() is public // error, name is private Shop 예제 3 앞의가게예제에서여러명의고객에대한판매를처리할수있도록확장한다. 각고객의장바구니를표현하는 Basket 클래스가추가되어있다. 장바구니에서선택한상품들의코드는 char * 배열로표현된다. 그리고장바구니안의상품개수는정수 n으로표현한다. 멤버함수 genbasket() 은임의의개수의코드들을생성한다. 임의의코드를생성하기위해멤버함수 gencode() 가사용된다. process() 함수는장바구니안의상품들을처리하여영수증을출력한다. 멤버함수 add(char const *code) 는파라미터로전달되어온 code를장바구니에추가한다. MerchandiseTable에서상품종류의개수는정수 n으로나타낸다. 즉, sentinel 값 대신 n을사용하여테이블크기를나타낸다. 이에맞추어멤버함수들의구현이조정되어있다
34 #include <iostream> #include <fstream> using namespace std; #include <string.h> #include <stdlib.h> // for rand() #include <ctype.h> // for tolower() struct CatalogEntry { char code[10]; char name[20]; int price; ; class MerchandiseTable { CatalogEntry tab[10]; int n; void add(char const *, char const *, int); public: void init(char const * filename); void print(); char const *getname(char const *code); int getprice(char const *code); ; void MerchandiseTable::add(char const *code, char const *name, int price) { if (n < 10) { strncpy(tab[n].code, code, 10); strncpy(tab[n].name, name, 20); tab[n].price = price; n++; void MerchandiseTable::init(char const * filename) { ifstream file(filename); char code[10], name[20]; int price; n = 0; while (file >> code) { file >> name >> price; add(code, name, price);
35 void MerchandiseTable::print() { cout << "Merchandise Table (size: " << n << ")" << endl << endl; for (int i = 0; i < n; i++) cout << tab[i].code << ' t' << tab[i].name << ' t' << tab[i].price << endl; cout << endl; char const * MerchandiseTable::getname(char const *code) { for (int i = 0; i < n; i++) if (_stricmp(tab[i].code, code) == 0) return tab[i].name; return 0; int MerchandiseTable::getprice(char const *code) { for (int i = 0; i < n; i++) if (_stricmp(tab[i].code, code) == 0) return tab[i].price; return 0; class Basket { char const *items[10]; int n; char const *gencode(); public: void add(char const *); void genbasket(); void process(merchandisetable&); ; char const *Basket::gencode() { switch(rand() % 3) { case 0: return "m01"; case 1: return "m02"; case 2: return "m04"; void Basket::add(char const *code) { if (n < 10) { items[n] = code; n++;
36 void Basket::genbasket() { n = 0; for (int m = rand() % ; m > 0; m--) add(gencode()); void Basket::process(MerchandiseTable& mtab) { int price; int total = 0; cout << endl << "RECEIPT" << endl << endl; for (int i = 0; i < n; i++) { total += price = mtab.getprice(items[i]); cout << items[i] << ' t' << mtab.getname(items[i]) << ' t' << price << endl; cout << endl << "total amount = " << total << endl; void main() { MerchandiseTable mtab; char reply[10]; mtab.init("catalog.txt"); mtab.print(); do { Basket b; b.genbasket(); b.process(mtab); cout << "Continue(Yes/No)? "; cin >> reply; while (tolower(reply[0])!= 'n'); cout << endl << "Closing NOW" << endl; < 실행결과 > Merchandise Table (size: 3) m01 soda 1000 m02 icecream 700 m04 chocolate
37 RECEIPT m04 chocolate 2000 m02 icecream 700 total amount = 2700 Continue(Yes/No)? RECEIPT m04 chocolate 2000 total amount = 2000 Continue(Yes/No)? RECEIPT m01 soda 1000 m01 soda 1000 m02 icecream 700 m04 chocolate 2000 m04 chocolate 2000 total amount = 6700 Continue(Yes/No)? Closing NOW 2.3 생성자 (constructor) 와소멸자 (destructor) C++ 클래스에는두가지의특별한함수가포함될수있으며, 이들은생성자와소멸자이다. 생성자 생성자함수는소속클래스와동일한이름을갖도록규정되어있다. 생성자에는결과값타입을표시하지않으며, 심지어 void조차도사용할수없다. 파라미터를갖지않는생성자는기본생성자 (default constructor) 라고불린다. 생성자는파라미터들을가질수도있다. 클래스의인스턴스, 즉, 객체가생성될때그클래스의생성자가호출된다. 생성자의일반적인역할은데이터멤버들에대한초기화와객체표현에필요한메모리할당등의작업이다. 복소수예제
38 #include <iostream> using namespace std; class Complex { double re, im; public: Complex(); Complex(double, double); Complex add(complex); Complex subtract(complex); void print(); ; // default constructor // 2nd constructor Complex::Complex(): re(0), im(0) { // Complex::Complex() { re = 0; im = 0; 와기능적으로동일함 // 즉, re 와 im 의값이 0 으로초기화된다는점에서는동일하다. // 그러나실행시간등에서앞의표현이유리할수있다. Complex::Complex(double r, double i): re(r), im(i) { Complex Complex::add(Complex b) { Complex c; c.re = re + b.re; c.im = im + b.im; return c; Complex Complex::subtract(Complex b) { return Complex(re - b.re, im - b.im); void Complex::print() { cout << re; if (im < 0) cout << " - " << (-im); else cout << " + " << im; cout << " i n"; void main() { Complex a(5, 3); Complex b(4, 7); Complex c; c = a.add(b); c.print(); a.subtract(b).print(); i 1-4 i
39 클래스에어떠한생성자도정의해주지않을경우, C++ 컴파일러가기본생성자를만들어준다. 컴파일러가제공하는기본생성자는부모클래스가있을경우부모클래스생성자를호출하고데이터멤버중다른클래스객체들이있을경우이들을위한생성자를호출해준다. (2.7절상속참고 ) 소멸자 소멸자함수는해당객체가더이상존재하지않게될때호출된다는점에서생성자의정반대이다. 소멸자함수이름은클래스이름앞에 ~(tilde) 기호를붙여표시한다. 소멸자는리턴값도없지만파라미터도갖지않는다. 아래예제코드를살펴보자. #include <iostream> using namespace std; #include <string.h> class Test { public: Test(); Test(char const *name); ~Test(); private: char *n; ; // constructor with no argument // constructor with an argument // destructor // name field char *strdup(char *s) { return strcpy(new char[strlen(s) + 1], s); Test::Test() { n = strdup("without name"); cout << "Test object without name created n"; Test::Test(char const *name) { n = strdup(name); cout << "Test object " << n << " created n";
40 Test::~Test() { cout << "Test object " << n << " destroyed n"; delete n; Test 클래스의객체들에이름을부여함으로써이객체들의생성과소멸을관찰할수있다. 생성자함수를위한인수는변수명다음에오는괄호안에표시함을유의하라. Test globaltest("global"); void func() { Test functest("func"); int main() { Test maintest("main"); func(); return 0; 위의프로그램의실행결과는아래와같다. 즉, 전역변수 globaltest, main() 함수의지역변수 maintest, func() 함수의지역변수 functest 등이차례로생성되며, 소멸순서는생성순서의역순이다. Test object global created Test object main created Test object func created Test object func destroyed Test object main destroyed Test object global destroyed 2.4 friend 함수와 friend 클래스 데이터멤버들에 public 가시성을부여하는것은일반적으로위험한일로간주된다. 그러나 private으로선언된데이터멤버들에제한적인외부접근을허용할필요가있는경우가있다. 이러한경우그클래스에서특정클래스나전역함수에대해 friend라고선언할수있다. friend로선언된클래스의멤버함수나 friend로선언된전역함수에서는해당클래스의 private 멤버들에자유롭게접근할수있다. 아래의예를살펴보자. ( 코드길이를줄이기위해모든함수는 inline 방식으로표현되어있다.)
41 class A { public: A(int v) { value = v; int getval() { return value; private: int value; ; void decrement(a& a) { a.value--; class B { public: void touch(a& a) { a.value++; ; 위의코드에서전역함수 decrement() 와클래스 B의멤버함수 touch() 안에서클래스 A 의 private 데이터멤버인 value에접근을시도하고있으므로컴파일오류가발생한다. 그러나아래와같이클래스 A 안에서클래스 B와전역함수 decrement() 를 friend로선언하게되면, 위의문제는해소된다. class A { public: friend class B; friend void decrement (A& what);.. ; friend 관계는상호적인것은아니다. 위의예에서클래스 B가클래스 A의 friend로선언되었지만, 클래스 A에서클래스 B의 private 멤버에접근이허용되는것은아니다. friend 함수의예 아래의예에서는벡터를나타내는클래스 Vector와정방행렬을나타내는클래스 SqMatrix 를정의하고있으며, 벡터연산과행렬연산등을포함할수있을것이다. 벡터와행렬사이의곱셈연산이필요할경우, 이러한연산을정의하는함수에서는 Vector의 private 데이터와 SqMatrix의 private 데이터모두에접근할수있어야한다. 이함수를두클래스중어디에두던다른클래스의 private 데이터에접근할수없다. 이러한문제를해소하기위한방편으로아래와같이해당연산을위한전역함수 multiply() 를두고양쪽클래스모두
42 에서 multiply() 함수를 friend 로선언하고있다. // implements vector of size 10 class Vector { int vec[10], n; public:... friend Vector multiply(vector&, SqMatrix&);... ; // implements 10x10 square matrix class SqMatrix { int mat[10][10], n; public:... friend Vector multiply (Vector&, SqMatrix&);... ; // implements vector-matrix multiplication Vector multiply (Vector& v, SqMatrix& m) { Vector p; for (int i = 0; i < v.n; i++) { p.vec[i] = 0; for (int j = 0; j < v.n; j++) p.vec[i] = v.vec[j] * m.mat[i][j]; return p; 2.5 연산자오버로딩 모든표준 C++ 연산자는클래스에대해오버로딩될수있다. 즉, 해당클래스객체에대해연산자의의미를새롭게정의할수있다. 기존의 C++ 연산자들에대해서만오버로딩이가능하며, 이경우해당연산자의우선순위나결합규칙은원래대로유지된다. 연산자오버로딩의정의방식은일반함수와동일하며, 다만함수이름이연산자앞에 operator라는단어를붙여만들어진다. 오버로딩된연산자는함수처럼호출될수도있지만, 원래연산자처럼식의표현에사용하는것이일반적인용법이다. 예를들면, + 연산자가오버로딩된경우, operator+(a, b) 처럼호출될수도있지만, a + b와같이사용하는것이원래의취지를살린용법이다
43 복소수예제 2 연산자 -, + 등에대해이항연산, 단항연산등을정의한다. 실수 + 복소수연산의경우에는 friend 함수로구현한다. 멤버함수형태로는구현할수없는데, 멤버함수의경우에는연산자앞에 Complex 객체가나타나야하기때문이다. 출력스트림에대한삽입연산자 (<<) 에 Complex 객체처리기능을추가하는것도연산자오버로딩을통해구현할수있으며, 이경우멤버함수대신 friend 함수로구현된다. 멤버함수의경우에는연산자앞에 Complex 객체가나타나야하는데, << 앞에는출력스트림객체가와야하기때문이다. #include <iostream> using namespace std; class Complex { double re, im; public: Complex(); Complex(double r, double i); Complex operator-(); friend Complex operator-(complex, Complex); friend Complex subtract(complex, Complex); friend Complex operator+(complex a); Complex operator+(complex); friend Complex operator+(double, Complex); void print(); friend ostream& operator<<(ostream&, Complex); ; Complex::Complex(): re(0), im(0) { Complex::Complex(double r, double i): re(r), im(i) { Complex Complex::operator-() { return Complex(- re, - im); Complex operator-(complex a, Complex b) { return Complex(a.re - b.re, a.im - b.im); Complex subtract(complex a, Complex b) { return Complex(a.re - b.re, a.im - b.im); Complex operator+(complex a) { return a;
44 Complex Complex::operator+(Complex b) { return Complex (re + b.re, im + b.im); Complex operator+(double a, Complex b) { return Complex (a + b.re, b.im); void Complex::print() { cout << re; if (im < 0) cout << " - " << (-im); else cout << " + " << im; cout << " i" << endl; ostream& operator<<(ostream& out, Complex a) { out << a.re; if (a.im < 0) out << " - " << (-a.im); else out << " + " << a.im; out << " i" << endl; return out; void main() { Complex a = Complex(2,3); Complex b = -a; // Complex b = a.operator-(); Complex c; c = subtract(a,b); c = a - b; // c = operator-(a,b); c.print(); c = a + b; // c = a.operator+(b); c.print(); cout << 3 + c; i i i 위의생성자함수에서데이터멤버초기화를위해함수헤딩끝부분에 ":re(0), im(0)" 와같은표현을사용하고있는데, 이는 re와 im의값을각기 0으로초기화한다는뜻이다. 아래의두생성자함수정의는내용상으로는차이가없다. Complex::Complex(): re(0), im(0) { Complex::Complex() { re = 0; im = 0;
45 그러나위의두생성자함수에서의초기화표현중위의표현을권장하는데, 이방식이실행시간에있어더효율적이기때문이다. 아래의표현이사용될경우생성자본체에들어가기전에데이터멤버들에대한기본초기화가일어나는데, 이로인해 re, im 등의데이터멤버에대한초기화가이중으로이루어지게된다. 2.6 자기참조를위한 this 멤버함수가적용될객체안의데이터멤버에접근하는것은소속클래스의데이터멤버를사용하여표현할수있다. 그러나그객체를가리키는주소, 즉, 포인터를사용하기위해서는특별한표현이필요하며, C++ 언어에서는 this라는키워드가그와같은경우에사용된다. 클래스 X 안에서사용되는 this의타입은 X* 가된다. this가필요한상황의예로이중연결리스트를나타내는클래스 Dlink의노드삽입을위한함수 insert() 를살펴보기로하자. class Dlink { Dlink* pred; Dlink* succ; // pointer to the predecessor // pointer to the successor public: Dlink(); void insert(dlink*);... ; 오른쪽그림에서 node1, node2 등을포함하는이중연결리스트가실선화살표들이나타내는포인터들에의해구성되어있다. node1과 node2 사이에포인터 ptr이가리키는노드를삽입하기위해 node1.insert(ptr) 과같은함수호출을사용한다고하자. 이러한삽입의결과로현재 node1과 node2를연결하는포인터값대신점선이나타내는연결관계로변경되어야한다. 이러한삽입을위한포인터연결순서를점선옆의숫자들이나타낸다고하면, insert() 함수는다음과같이정의될수있을것이다. 아래의코드에서두번째명령에서 p->pred에지정될값으로 insert() 함수가적용되는 node1의주소가필요하며, insert() 함수안에서 &node1과같은표현을사용할수는없다. 따라서이를위해 this와같은표현이필요한것이다
46 // 현재노드다음에포인터 p 가가리키는노드를삽입한다. void Dlink::insert(Dlink* p) { p->succ = succ; // (1) p->pred = this; // (2) succ->pred = p; // (3) succ = p; // (4) 2.7 상속 (inheritance) 새로운클래스 B를정의해야하는데기존의클래스중유사한기능의클래스 A가있다고하자. 이경우클래스 A를위한코드를활용할수있는데, 이를코드재사용 (code reuse) 이라고한다. 전통적인코드재사용방식은소스코드를편집하여새로필요한코드를만드는것인데, 이경우편집과정에서기존의올바른코드를잘못수정할수도있고, 버전관리문제도발생할수있다. 객체지향프로그래밍을지원하는언어들에서는코드재사용을위해클래스사이의상속기능을제공한다. 앞에서예로들고있는경우, 클래스 A가가지고있는데이터멤버나멤버함수를클래스 B에서상속받아사용하며, 클래스 A에없는부분만추가하거나일부멤버함수들을재정의하는등의방법을사용한다. 이경우기존클래스 A의내용은전혀수정하지않으며, 일반적으로클래스선언부만참조하고클래스구현부의함수정의코드는소스형태로는볼필요조차없다. 상속은소스편집방식에비해훨씬안전하고간편한코드재사용방식으로평가되고있다. C++ 에서클래스를상속받아정의하는것을파생 (derivation) 이라고한다. 파생에서원래의클래스를기본클래스 (base class) 또는부모클래스 (parent class) 라고한다. 새로정의되는클래스는파생클래스 (derived class) 또는자식클래스 (child class) 라고한다. 부모클래스, 부모클래스의부모클래스,... 등을조상클래스라고부르며, 반대의관계에있는클래스는자손클래스로부른다. 상속의예 먼저기본클래스로사용될클래스 List를정의한다. List 객체는여러개의숫자들의목록을나타내며, 이를위해숫자들을저장할배열 array[] 와배열안에들어있는숫자의개수를나타내는변수 count를두고있다. List 클래스의기본생성자함수는 count를 0으로초기화한다. 소멸자는하는일이전혀없으며, 따라서필요하지도않지만그냥두고있다. Insert(x, pos) 함수는숫자 x를 pos를인덱스로하는위치에끼워넣는데, 이를위해원래 pos 이후의위치에들어있던숫자들을한칸씩뒤로이동시킨다. Get(pos) 은 pos위치에있는값을리턴하며, Size() 는현재의숫자개수를리턴한다
47 #include <iostream> using namespace std; class List { int int array[100]; count; public: List(): count(0) { // count is set to 0 ~List() { void Insert(int x, int pos) { for (int i = count; i > pos; i--) array[i] = array[i - 1]; array[pos] = x; count++; int Get(int pos) { return array[pos]; ; int Size() { return count; void main() { List list; for (int i = 0; i < 10; i++) list.insert(i, i); list.insert(100, 5); list.insert(200, 7); list.insert(300, 0); // list: for (i = 0; i < list.size(); i++) cout << list.get(i) << endl; 위의 List 클래스에아래두가지기능을추가하여새로운클래스 SortedList를정의하려고한다. (1) 숫자들의목록이정렬된순서를유지할수있게숫자를끼워넣는삽입함수 SortedInsert() 를둔다. (2) 목록안의숫자들의합을구하는일이자주일어난다고하자. 합이필요할때마다목록안의모든숫자들의합을새로계산하는대신, 합을나타내는변수를두고매번숫자가삽입될때마다이변수를갱신한다. SortedList 클래스를처음부터새로정의하는대신이미만들어진 List 클래스를상속받
48 아목록표현을위한데이터멤버, 그리고멤버함수들을활용하는방식으로만든다. 먼저위의두가지기능중첫번째기능만먼저추가해보자. class SortedList: public List { public: SortedList():List() { SortedInsert(int x) { int i, v; ; i = 0; do { v = Get(i); if (v < x) i++; while (v < x && i < Size()); Insert(x, i); List 클래스자체에는아무변경도일어나지않으며, 단지 List 클래스의바탕위에새로운클래스 SortedList를정의한다. 상속의표현은다음과같은형식으로표시된다. derived-class : public base-class 파생클래스는기본클래스의전체데이터멤버를상속받지만, 기본클래스의 private 멤버에직접접근할수는없다. 필요할경우기본클래스에서제공되는 public 또는 protected 가시성을갖는멤버함수를통한접근만이가능하다. 멤버함수의경우 private 가시성을갖는함수이외의다른함수들을상속받는다. 상속표현에서콜론 (:) 기호다음에는가시성을나타내는 public, protected, private 중의하나를사용한다. public의의미는기본클래스에서상속받은 (public, protected) 멤버들의가시성을파생클래스에서도그대로유지한다는의미이며, 대부분의상속에서는 public을사용한다. protected를사용하면기본클래스에서상속받은 (public, protected) 멤버들의가시성이파생클래스에서는 protected가된다. private를사용하면기본클래스에서상속받은 (public, protected) 멤버들의가시성이파생클래스에서는 private가된다. 이상속에서파생클래스 SortedList의기본클래스는 List이다. 생성자함수의헤딩뒤에기본클래스생성자함수호출이올수있다
49 아래다이어그램이위의상속관계를표현한다. 합계산기능의보완을반영하여완성된 SortedList 클래스는아래와같다. class SortedList: public List { private: int total; public: SortedList(): List(), total(0) { void Insert(int x, int pos) { total += x; List::Insert(x, pos); int GetTotal() { return total; SortedInsert(int x) { int i, v; ; i = 0; do { v = Get(i); if (v < x) i++; while (v < x && i < Size()); Insert(x, i);
50 파생클래스인 SortedList 안의 Insert() 함수는 List 클래스와동일한파라미터를갖는다. 따라서이는 List 클래스에서상속받은 Insert() 를대체하게되며, 이를멤버함수재정의 (overriding) 이라고한다. SortedList의 Insert() 함수안에서기본클래스에서정의된 Insert() 함수를호출하기위해서는함수이름앞에소속클래스를지정하기위한 List:: 표현이필요하다. SortedInsert() 함수끝의 Insert() 함수는 SortedList::Insert() 함수이다. 2.8 가상함수 (virtual function) 이절의예제코드에서는 4개의클래스가아래왼쪽그림과같이상속관계에의한계층구조를형성하고있으며, 각클래스의객체구조는아래오른쪽그림과같다. 클래스상속계층구조 클래스객체구조 main() 함수에서는위의각클래스의객체들을정의하고이들의 next 필드를사용하여연결리스트를아래그림과같이구성하고있다. &e 각클래스마다객체구조를구성하는데이터멤버들이다르며, 이들클래스안에각기정의되어있는멤버함수 print() 에서는각자의객체구조에포함된내용을출력하고있다. 함수 print_list() 에서는위의연결리스트의각노드에대해 print() 함수를적용하고있는데, ll->print() 라는함수호출에서 ll의타입은 (employee *) 이지만, ll이가리키는객체의실제타입은각노드마다다르다. 이때실제노드유형에맞는 print() 함수를사용할것인가, 또는포인터변수 ll의타입에따라항상 employee 클래스의 print() 함수를사용할것인가는 print() 함수가일반멤버함수인지, 가상함수인지에따라달라진다
51 아래의코드에서는 print() 함수를정의하고있는가장상위의클래스인 employee에서 print() 함수선언에 virtual이라는키워드를사용하고있고, 이는이함수가가상함수임을나타낸다. print() 함수의경우와같이가상함수로선언되어있고, 또포인터변수를통해그함수를호출하고있을경우에는실제객체의타입에맞는함수가불려나오게된다. 따라서 print_list() 함수의출력결과는다음과같게된다. K. Lee salary: Gwangsoo level 10 dept: Sales G. Rhee level 9 J. Brown 만일 print() 함수가가상함수가아니었다면 print_list() 함수안에서매번호출되는 print() 함수는이름만출력하는 employee::print() 이었을것이며, 그경우출력결과는아래와같게되었을것이다. K. Lee Gwangsoo G. Rhee J. Brown 예제코드 #include <iostream> using namespace std; struct employee { employee* next; char* name; virtual void print(); ; void employee::print() { cout << name << " n"; struct manager : public employee { int level; void print(); ;
52 void manager::print() { employee::print(); cout << " tlevel " << level << " n"; struct director : public manager { char* dept; void print(); ; void director::print() { manager::print(); cout << " tdept: " << dept << " n"; struct worker : public employee { double salary; void print(); ; void worker::print() { employee::print(); cout << " tsalary: " << salary << " n"; void print_list(employee* ll) { for (; ll; ll = ll->next) ll->print(); void main() { employee e; e.name = "J. Brown"; e.next = 0; manager m; m.name = "G. Rhee"; m.level = 9; m.next = &e; director d; d.name = "Gwangsoo"; d.level = 10; d.dept = "Sales"; d.next = &m; worker w; w.name = "K. Lee";
53 w.salary = 10000; w.next = &d; print_list(&w); cout << " n"; m = d; m.print(); e = d; e.print(); 상속관계에있는클래스의객체들을가리키는포인터의경우자손클래스객체에대한포인터값을조상클래스포인터변수에저장하는것은일반적으로허용되며, 이때조상클래스포인터변수가가리키는자손클래스객체는원래의객체구조를그대로유지한다. 포인터변수가아니라객체를나타내는변수들의경우에도자손클래스객체를조상클래스객체변수에저장하는것은일반적으로허용된다. 그러나이경우조상클래스가갖고있는데이터멤버의수가자손클래스보다적으며, 자손클래스에만나타나는데이터멤버들은이과정에서없어진다. 따라서이결과로자손클래스객체는조상클래스객체로변환되며, 아래그림에서이러한예두개를보이고있다. ( 위의 main() 함수의마지막부분참고 ) 이경우 m.print() 나 e.print() 는원래객체가소속된 director 클래스의 print() 함수대신각기 manager 클래스나 employee 클래스의 print() 함수가호출되며, 그출력결과는다음과같다. Gwangsoo level 10 Gwangsoo 참고로반대방향의저장, 즉, 조상클래스객체를자손클래스객체변수에저장하는것은일반적으로허용되지않는다. 만일그와같은값의저장이허용될경우오른쪽그림과같은상황이될텐데, 이경우 d의 dept 필드가정의되지않음을유의하라. 다시정리하자면, 상속관계에있는클래스들에서재정의되어있는멤버함수에대해각객체에맞는클래스의멤버함수를이용할수있기위해서는그멤버함수가가상함수로선언되어있어야하며, 또한함수호출이상위클래스포인터를통해이루어져야한다
54 2.9 기타 템플릿 (template) 템플릿은일종의메타클래스 ( 클래스의클래스 ) 이다. 클래스를바탕으로객체변수들이만들어지는것과마찬가지로, 템플릿을바탕으로클래스들이만들어진다. 템플릿에의해서만들어지는클래스들은구성원소의데이터타입만다를뿐알고리즘은동일하다예를들어, char를저장하는스택을클래스로구현했다면, 여기에 int나 double을저장할수없다. int나 double을저장하는스택도각각클래스로구현했다면이들클래스는저장하는데이터타입인 char, int, double만다를뿐소스코드는스택알고리즘이므로동일할것이다. 이경우, 스택을템플릿클래스로구현하고, 이템플릿클래스를가지고필요에따라 char, int, double 등의데이터를저장하는클래스를자동으로생성하여사용할수있다. 즉템플릿클래스란특정데이터타입과무관하게만들어진메타클래스이다. 그때그때필요한데이터타입을템플릿파라미터로전달하여템플릿클래스로부터실제클래스를생성하여사용한다. #include <iostream> using namespace std; template <class T> class Stack { T store[100]; int top; public: Stack(); void push(t val); T pop(); ; template <class T> Stack<T>::Stack() : top(0) { template <class T> void Stack<T>::push(T val) { store[top++] = val; template <class T> T Stack<T>::pop() { return store[--top];
55 void main() { Stack<int> istack; istack.push(3); istack.push(5); istack.push(7); cout << istack.pop(); cout << istack.pop(); cout << istack.pop() << endl; Stack<char *> sstack; sstack.push("abc"); sstack.push("pqr"); sstack.push("xyz"); cout << sstack.pop(); cout << sstack.pop(); cout << sstack.pop() << endl; 753 XYZPQRABC 템플릿은아래예에서와같이함수정의에도적용될수있다. #include <iostream> using namespace std; template <class T> void swappy(t& x, T& y) { T t; t = x, x = y, y = t; void main() { int a = 3, b= 5; swappy(a, b); cout << a << ", " << b << endl; double p = 3.3, q = 5.5; swappy(p, q); cout << p << ", " << q << endl; 5, 3 5.5, C++ 키워드 C/C++ 공통 (33 개 ) C++ 전용 (15개) 최근추가된 C++ 전용 (25개) asm auto break case char const continue default do double else enum extern float for goto if int long register return short signed sizeof static struct switch typedef union unsigned void volatile while catch class delete friend inline new operator private protected public template this throw try virtual and_eq alignof bitand char16_t char32_t compl constexpr const_cast decltype dynamic_cast explicittodo false mutable noexcept not_eq nullptr or_eq reinterpret_cast static_assert static_cast thread_local typeid wchar_t xor_eq true
56 연습문제 1. C 의 struct 와 C++ struct 의가장중요한차이점은무엇인가? 2. C++ 언어에서 struct 와 class 의차이는무엇인가? 3. 가시성수식어 public, protected, private 를구분하여설명하라. 4. C++ 컴파일러가만들어주는기본생성자는어떤일들을하는가? 또어떤경우에 C++ 컴파일러가기본생성자를만들어주는가? 5. 멤버함수의가시성이 private 가될수있는지에대해논하라. 6. friend 메커니즘이란무엇인가? 7. 함수에대한오버로딩 (overloading) 과재정의 (overriding) 를구분하여설명하라. 8. 가상함수를설명하라. 9. (1) 전통적인코드재사용방법과객체지향적언어에서의 inheritance를사용할때의가장중요한차이점한가지를쓰라. (2) 전통적인방법을사용했을때발생할수있는가장큰문제점두가지를쓰라. 10. 아래의코드에서 Array 클래스는동적크기의정수배열을구현하는데, 그크기는 Array 객체생성시에정해진다. 이클래스정의는불완전한데, 이를완성시켜가기위해아래질문들에답하라. #include <iostream> using namespace std; class Array { int int *a; sz; public: Array (int size = 10); Array (Array& b); int get (int loc, int x); int put (int loc, int x); ;
57 Array::Array (int size) { a = new int [sz = size]; Array::Array (Array& b) { copy (b); // return 1 on error; the result is passed through x int Array::get (int loc, int x) { if (loc >= 0 && loc < sz) { x = a[loc]; return 0; else return 1; // return 1 on error int Array::put (int loc, int x) { if (loc >= 0 && loc < sz) { a[loc] = x; return 0; else return 1; void main () { int n; Array a(5); a.put(0, 10); a.put(1, 20); a.put(2, 30); a.put(3, 40); Array b = a; b.get(3, n); cout << "n = " << n << " n"; b.put(4, 50); a = b; a.get(4, n); cout << "n = " << n << " n"; ( 가 ) 위의코드에서멤버함수 copy(b) 는 Array 객체 b의내용을복사하여현재의객체를만들도록정의하라. ( 나 ) 이클래스를위해적절한소멸자를정의하라
58 ( 다 ) 이클래스를위해적절한 = 연산자를정의하라. ( 라 ) 멤버함수 get() 은기대대로동작하지않는다. 문제점을찾아설명하고, 함수를수정하라. ( 마 ) 함수 get() 을 Array 클래스의 friend 함수로만들기로결정하였다고하자. 이함수를다시정의하고, 클래스정의및 main() 함수에필요한변경을표시하라. ( 바 ) main() 함수에서 a.put(0.10) 등을 a[0] = 10 등으로, 그리고 a.get(4, n) 등은 n = a[4] 등과같이표시할수있도록하기위해, 클래스 Array에필요한내용을추가하라. 11. 아래의코드에서 Vector 클래스는 3차원벡터를나타낸다고하자. 아래의질문에따라 Vector 클래스를완성시키되, 가장간단하게구현하여야한다. #include <iostream> using namespace std; #include "Vector.h" int main() { Vector v1(2.0, 3.0, 4.0); Vector v2(5.2, -2.1, 4.3); cout << "v1 = " << v1 << endl; cout << "v1 + v2 = " << v1 + v2 << endl; return 0; v1 = [2, 3, 3] v1 + v2 = [7.2, 0.9, 0.9] ( 가 ) 위의프로그램의실행결과가우측하단의사각형에표시된것과같이될수있도록 Vector 클래스를정의하는헤더파일을작성하라. ( 나 ) Vector 클래스를구현하는 C++ 소스파일을작성하라
59 3 장 MFC 프로그래밍 윈도우응용프로그램개발을용이하게할수있도록 Visual C++ 에서는 MFC 클래스라이브러리와응용프로그램마법사 (AppWizard), 클래스마법사 (ClassWizard), 리소스편집기 (Resource Editor) 등의다양한도구를제공하고있다. 이장에서는응용프로그램마법사등의도구들은사용하지않고 MFC만을사용하여윈도우응용프로그램을작성하는방법을설명한다. 여기에서는 MFC 프로그램을작성, 컴파일, 실행하는방법을먼저소개한다음, MFC와윈도우응용프로그램의프로그램구조를설명한다. 그런다음 MFC의 GUI 요소인컨트롤과컨트롤의속성조정등을다룬다. 끝으로 MFC에서이벤트처리를위해사용되는메시지맵을이용하는방법을소개한다. 이장은다음과같은절들로구성되며, 예제프로그램들은 Visual Studio 2010을기준으로설명되어있다. 1. MFC 개요 2. MFC 프로그램의작성, 컴파일, 실행 3. MFC 프로그램예제 4. MFC 스타일 5. 메시지맵 (Message Map) 3.1 MFC 개요 Visual C++ 는단순한컴파일러가아니다. Visual C++ 는적절히활용하면훌륭한윈도우응용프로그램을작성하는데도움이되는여러가지도구들과 MFC(Microsoft Foundation Class) 라이브러리등을포함하고있다. MFC는윈도우 API의사용자인터페이스측면을캡슐화하고있으며, 객체지향적방식으로윈도우응용프로그램작성을용이하게만든다. MFC는모든윈도우운영체제에서사용될수있어코드의이식성 (portability) 을보장한다. MFC 라이브러리란무엇인가? 텍스트편집기, 그림편집기, 파일검색프로그램, 데이터상관관계그래프표현프로그램등다양한윈도우응용프로그램을생각해볼수있을것이다. 이러한윈도우응용프로그램을개발하기위해서는어디에서부터시작할것인가? 전통적인프로그램개발의경우필요한처리과정을점차세분화하여전체프로그램의처리절차에대한구조도를작성하고충분히세분화된프로시저의구현을위한알고리즘을순서도나모의코드등으로표현한다. 물론필요한자료구조나파일구조등을정하는일도설계과정에포함될것이다
60 윈도우응용프로그램의경우에도이러한사항들에대한고려가필요하겠지만, 이보다는응용프로그램의초기화면에서부터프로그램이진행되는동안나타날여러화면을설계함으로써프로그램의내용을구체화시켜나가는것이오히려일반적인접근방법이다. 이러한화면은창 (window) 과창안에들어가는버튼, 체크박스, 스크롤바, 리스트박스, 메뉴등등의사용자인터페이스를나타낸다. 대규모프로그램의경우에는많은사람들이설계에참여하여검토와수정을반복하는복잡한과정을거치겠지만, 간단한프로그램의경우는메모지나종이냅킨등을사용하여짧은시간만에사용자인터페이스설계가완료될수도있다. 설계가완료되면다음단계인코드구현단계로진행한다. 마이크로소프트사에서는윈도우응용프로그램에서윈도우운영체제기능을이용하여창이나컨트롤등의생성, 파일시스템에접근, 통신기능, 프로세스관리기능등등을이용하기쉽게 C 함수형식의윈도우 API(Windows Application Program Interface) 를제공하고있다. 윈도우 API는윈도우 API 설명서에기술되어있는수많은 C 함수로이루어져있다 년대초반에사용되었던 16 비트운영체제인윈도우 3.1을위한윈도우 API와 32 비트운영체제인윈도우 NT/95/98/2K/XP 등의 32 비트운영체제용 API는차이가있으며, 특히 32 비트운영체제용 API를구분하기위한표현으로 Win32 API라고부른다. 이러한 API들은 C++ 에서사용될수도있지만, C나다른언어에서도이용될수있다. 마이크로소프트사는또한윈도우 API에기초한 C++ 라이브러리인 MFC를제공하고있는데, 윈도우 API를사용하는것보다 MFC를사용하는것이프로그래머의작업을훨씬쉽게만들어준다. MFC를사용하는것이훨씬덜복잡할뿐만아니라상속이나캡슐화등 C++ 언어의객체지향적특성도이용할수있다. 또윈도우 3.1 시스템에서개발된코드를 32 비트윈도우에이식하기도쉽다. 따라서 MFC는윈도우 API에대해상대적으로권장되는윈도우응용프로그램개발방법이다. MFC를이용하여창이나컨트롤등의사용자인터페이스요소들을만들고이들의외형이나동작을응용프로그램에맞게조정할수있다. 또한사용자가컨트롤을조작할때의반응으로수행될코드도작성할수있다. 예를들어사용자가버튼을클릭하거나리스트박스항목을선택하거나했을때적절히반응하는코드가필요할것이다. 이러한이벤트처리코드들은윈도우응용프로그램에서복잡한부분이지만, MFC는이러한코드들을간명하게표현할수있게도와준다. Visual C++ 개발환경은 MFC의사용에맞추어져있다. 따라서 Visual C++ 를이용한윈도우응용프로그램의개발을위해서는 MFC에대한이해가필수적이다. 윈도우용어 : 컨트롤, 창, SDI/MDI 복잡한항공기나우주선등을조종하기위해버튼, 다이얼, 핸들등을사용한다. 윈도우응용프로그램은스스로동작하기보다는사용자와의상호작용을통해작업이진행되며, 이러한상호작용을위해이용되는사용자인터페이스요소들이있다. 이들을컨트롤이라고부르며, 16 비트윈도우시절부터사용되어온컨트롤들을표준컨트롤이라고부르며, 이에는다음과같은 8가지컨트롤이있다
61 텍스트레이블 (static text label) 버튼 (push button) 라디오버튼 (radio button) 체크박스 (check box) 리스트박스 (list box) 콤보박스 (combo box) 에디트 (editable text areas; single-line/multi-line) 스크롤바 (scroll bar) 표준컨트롤들외에도공통컨트롤 (common control), custom 컨트롤, 액티브X 컨트롤등의여러컨트롤들이사용되고있다. 윈도우 95 운영체제는 15개의새로운컨트롤들을추가하였는데, 이들을공통컨트롤이라부르며, 애니메이트, 스핀, 프로그레스, 슬라이더등의컨트롤들이포함된다. 또프로그래머가컨트롤을정의할수도있다. 위의그림에서여러가지컨트롤들을보여주고있다. 오른쪽의작은그림에는각컨트롤마다번호를부여하고있다. 이번호는탭순서번호 (tab order) 라고하는데, 대체로컨트롤들이만들어진순서에따라부여되며, 사용자들이탭키를사용하여이동하는컨트롤순서를나타낸다. 물론텍스트레이블등의일부컨트롤은사용자의입력을받지않으므로탭키에의한이동순서에서건너뛰게될것이다. 이번호들이나타내는또다른의미는컨트롤의단위이다. 예를들어라디오버튼이나체크박스의경우버튼 / 박스표시와옆의설명텍스트가하나의컨트롤을구성하며, 따라서하나의번호가버튼그림과옆의텍스트를나타낸다. 반면, 스크롤바, 프로그레스바, 슬라이더등은컨트롤안에설명텍스트를갖지않는다. 따라서아래그림에서이러한컨트롤주위에표시된
62 레이블은텍스트레이블이라는별개의컨트롤임을알수있다. 특히, 스핀컨트롤의경우하나의컨트롤처럼보이는부분이실제로는에디트컨트롤과스핀컨트롤이결합된두개의컨트롤로구성되어있다. 컨트롤들을쉽게생성하는방법은리소스편집기라는도구를사용하는것인데, 이를위해서는프로젝트생성단계에서 AppWizard라는도구를사용해야한다. 이러한방법은 4장이후에서다루게되며, 여기서는번거롭지만프로그램소스코드를통해컨트롤을생성하는방법을사용한다. 이경우컨트롤의크기, 위치등을나타내는값들을미리계산하여프로그램소스코드안에표현하게된다. 윈도우운영체제는여러유형의응용프로그램창을지원한다. 응용프로그램창의주요유형에는프레임창 (frame window) 과다이얼로그박스 (dialog box; 대화상자 ) 가있다. 프레임창은크기변경, 최대화, 최소화등을포함하는많은기능을지원하는창유형이다. 다이얼로그박스는크기조절기능이없는창이며, 여기에는 modal 다이얼로그박스와 modeless 다이얼로그박스라는두종류가있다. modal 다이얼로그박스가화면에나타나면, 이박스의처리를끝내고확인버튼이나취소버튼등을클릭하여화면에서사라지게만들때까지동일응용프로그램의다른부분으로이동할수없다. 물론다른응용프로그램으로옮겨가는것은제한하지않는것이일반적이다. modeless 다이얼로그박스는이와같은제한이없어다이얼로그박스가나타나있는경우에도동일응용프로그램의다른부분으로옮겨갈수있다. 응용프로그램의주실행창은프레임창의형태인경우도있고다이얼로그박스형태인경우도있다. 또응용프로그램의실행중에나타나는보조창도프레임창도사용되고다이얼로그박스도사용된다. 보조창으로나타나는다이얼로그박스의경우대체로는 modal 박스이다. HWP 프로그램의경우파일불러오기, 환경설정, 문단모양등수많은기능의수행을위한다이얼로그박스들이있으며, 이들은대체로 modal 다이얼로그이다. 그러나 HWP에서찾기또는바꾸기등의경우나타나는다이얼로그박스와같이 modeless 박스가사용되는경우도있다. 대부분의윈도우응용프로그램은실행을통해발생한데이터를파일에저장해두었다가다음에실행할때그파일을다시불러들여이전의작업내용을되살린다. 이때파일에저장되는내용을프로그램실행중메모리안에나타내는자료구조가존재하게되며, 이자료구조는파일의전체내용을나타낼수도있고현재사용자가관심을갖는파일의일부만을나타낼수도있다. 이러한메모리상의파일에대응되는자료구조를문서 (document) 라고부르는데, 때로파일자체를가리키는표현으로사용되기도한다. 비교적간단한윈도우응용프로그램들은한종류의문서만을취급한다. 메모장이이러한응용프로그램의대표적인경우이며, 이러한응용프로그램들은대체로한번에한개의문서만열어작업한다. 이러한응용프로그램유형을 Visual C++ 에서는 SDI(Single Document Interface) 응용프로그램이라고한다. HWP, MS 엑셀, MS Visual Studio 등의경우에는한번에여러개의문서를동시에열어둔상태에서작업을진행할수있으며, 이러한프로그램들중에는취급할수있는문서유형이두종류이상일수도있다. HWP의경우.HWP 확장자를갖는 HWP 고유문서를편집할
63 수도있지만,.txt 파일이나.html 파일등의문서를편집할수도있다. 이러한응용프로그램유형은 MDI(Multiple Document Interface) 응용프로그램으로불린다. 모든윈도우응용프로그램이문서를사용하는것은아니다. 윈도우응용프로그램중에는작업결과를전혀저장하지않는계산기나게임프로그램들도있다. 이벤트구동형소프트웨어및관련용어 대부분의윈도우기반 GUI들은동일한기본요소들을포함하고있으며, 이요소들의동작방식도거의같다. 화면에는창들이나타나있을것이며, 창안에는컨트롤, 메뉴, 아이콘등이포함되어있고, 사용자는마우스나키보드로이들을조작할수있다. 사용자들이만나는인터페이스요소들은대부분의시스템들에서거의차이가없으며, 여기에는버튼, 스크롤바, 아이콘, 다이얼로그박스, 풀-다운메뉴등등이포함된다. 운영체제나응용프로그램에따라서는이러한요소들의모습에있어서는약간의차이가있을수도있다. 예를들면, 스크롤바의기본색상이나스크롤바버튼의종류나배열위치등은 MS 윈도우운영체제와애플컴퓨터의경우에도차이가있으며, 또응용프로그램마다차이가있을수도있다. 예를들어오른쪽모양의버튼들은스크롤바에나타나지않을수도있고, 나타나더라도모여있는경우와스크롤바의상단과하단에각기위치하는경우도있다. GUI를갖춘윈도우응용프로그램을만들기위해프로그래머는먼저필요한사용자인터페이스요소들을창안에배치한다. 예를들어화씨온도와섭씨온도사이의변환을위한온도변환프로그램을만든다고하자. 프로그래머는이러한프로그램이필요로하는사용자인터페이스요소들을선택하게될것인데, 오른쪽그림에서처럼온도들을입력받기위한에디트컨트롤들, 또에디트컨트롤들의역할을표시하는텍스트레이블컨트롤, 또프로그램종료를위해사용되는종료 (Quit) 버튼등을선택할수있을것이다. Temperature Conversion Program 이프로그램의사용자는변환할온도로서에디트컨트롤들에숫자를입력하거나, 프로그램을종료시키기위해종료버튼을클릭할수있다. 마우스와키보드를사용하여여러유형의컨트롤들에사용자가취하는동작들에대한반응은컨트롤마다또사용자의동작에따라달라진다. 온도변환프로그램의경우사용자가화씨온도입력용에디트컨트롤에숫자를입력하면새로입력된숫자가에디트컨트롤에나타나며, 동시에섭씨온도값을나타내는에디트컨트롤의값이변경될것이다. 종료버튼을클릭하면종료버튼테두리선바로안쪽에점선이나타나면서클릭동작에따라버튼이눌려졌다원래의모습으로돌아오는듯한모습을그리면서프로그램은종료하게된다. 이렇게사용자의동작을받는컨트롤자체의모습에대한변화와이에따른작업등이일어나는데, 컨트롤자체의모습변화는컨트롤자체가처리하게되며, 컨트롤입력에의한작업은보통함수로만들어져있다가컨트롤에대한동작에의해호출된다. 하나의컨트롤에대해여러가지동작이있을수있다. 이를테면아이콘에대한단일클릭의경우와더블클릭에대해호출되는함수는일반적으로다른경우가많다
64 사용자의컨트롤에대한동작은응용프로그램에대한어떤작업의요청이며, 이러한작업요청은이벤트 (event) 라는형태로다루어진다. 프로그래머는각컨트롤별로또해당컨트롤에발생할수있는이벤트별로반응을위한함수들을만들어주게된다. 윈도우응용프로그램은처리절차가미리정해져있고정해진순서에따라정해진형식의데이터를입력함으로써필요한계산결과를얻게되는전통적인프로그램과는수행방식이다르다. 전형적인윈도우응용프로그램은실행되면주실행창에사용자인터페이스요소들을생성한다음아무일도하지않고기다리고만있다. 이때사용자가컨트롤등에대해이벤트들을발생시킴으로써프로그램이수행하게될작업이결정되면서처리가진행된다. 이렇게수행되는프로그램을이벤트구동형소프트웨어 (event-driven software) 라고하며, 이러한프로그램의표현은전통적인프로그램과는다른측면이있다. 이장에서이벤트에대한처리는마지막절에서메시지맵메커니즘을사용하는방식으로설명될것이다. MFC 의구성 MFC는여러버전의윈도우운영체제에서호환성있게사용될수있도록설계된윈도우프로그래밍용클래스라이브러리이며, Visual C++ 6.0에서사용되는 MFC 버전 6.0의경우 225개의클래스들로이루어져있다. (Visual C 에서사용되는 MFC 버전 9.0의경우 393개의클래스들로이루어져있다.) MFC에포함되어있는주요클래스유형에는다음과같은것들이있으며, 이외의다른클래스들도있다. 응용프로그램프레임워크관련클래스 윈도우관련클래스 그래픽관련클래스 자료구조클래스 : 배열, 리스트, 맵 파일및데이터베이스관련클래스 인터넷관련클래스 OLE 관련클래스 예외처리및디버깅을위한클래스 물론모든윈도우프로그램에서이모든클래스들을이용하는것은아니다. 프로그램에따라서는이들중대여섯개이내의클래스만사용하는경우도많으며, 꽤복잡한프로그램의경우도이삼십개이상의클래스를사용하는경우는흔하지않다. 다음페이지에보여주고있는것은 MFC 버전 6.0의모든클래스들의계층구조이다. 이들중많은클래스는 CObject에서파생된클래스들이다. 그림의오른쪽에는 CObject에서파생되지않은클래스들을보여주고있다. 그뒤에는 3쪽에걸쳐 Visual Studio 2010의 MFC 9.0의계층구조를싣고있다. Visual Studio 2010에서도움말안에서 MFC에관한정보를찾으려면 [ 도움말 ] 메뉴항목을선택한후검색창에 "MFC Reference" 를입력한다. 또는다음순서에따라링크를클릭한다 : < 라이브러리홈 > - <Visual Studio 2010> - <Visual Studio> - <Visual Studio 언어 > - <Visual C++> - <Visual C++ 참조 > - <Visual C++ 라이브러리참조 > - <MFC Reference>
65 - 64 -
66 Visual Studio 2008/2010에서사용되는 MFC
67 - 66 -
68 - 67 -
69 아래표는이책에서사용되거나논의되는모든클래스들을유형에따라분류하여싣고있다. 클래스유형 클래스이름 CObject 응용프로그램프레임워크클래스표준컨트롤그래픽기타 CCmdTarget, CWinThread, CWinApp, CWinAppEx, CWnd, CFrameWnd, CMDIFrameWnd, CMDIFrameWndEx, CMDIChildWnd, CMDIChildWndEx, CDialog, CDialogEx, CView, CDocument CStatic, CButton, CListBox, CComboBox, CEdit, CScrollBar CDC, CWindowDC, CClientDC, CPaintDC, CMetaFileDC, CGdiObject, CPen, CBrush, CFont, CBitmap, CPalette, CRgn, CRectTracker CRect, CPoint, CDWordArray, CEditView CObject 클래스는대부분의 MFC 클래스들에공통된데이터멤버나멤버함수들을포함하고있다. CCmdTarget 클래스는 CObject의자식클래스이며, 이벤트에의해발생되는메시지를받아처리할수있는구조를제공하는클래스이다. CWinThread 클래스는 CCmdTarget의자식클래스이며, 실행스레드를지원한다. CWinApp 클래스는 CWinThread의자식클래스이며, 윈도우응용프로그램을나타낸다. CWnd 클래스는프레임창 (CFrameWnd), 다이얼로그박스 (CDialog), 컨트롤등에공통된기능을제공한다. 컨트롤도일종의창이라는점을유의하자. CDocument 클래스는문서를나타내는클래스이며, CView 클래스는사용자에게문서의내용을보여주고또사용자가문서에접근하는것을도와준다. 8 가지표준컨트롤을지원하기위해 6 개의클래스가사용되는데, 이중 CButton 클래스는버튼, 라디오버튼, 체크박스등의컨트롤을표현할수있다. 그래픽에관련된클래스에는그래픽명령을수행하는장치를나타내는클래스들과그림그리기도구를나타내는클래스들이있다. 그래픽장치관련클래스들에는장치에관한정보를갖고있으며, 여러가지그림그리기함수를제공하는기본클래스 CDC와그자식클래스인 CWindowDC, CClientDC, CPaintDC, CMetaFileDC 등이있다. 그리기도구를나타내는클래스들에는기본클래스인 CGdiObject와그자식클래스들인 CPen, CBrush, CFont, CBitmap, CPalette, CRgn, CRectTracker 등의클래스가있다. 기타클래스로는사각영역의위치와크기를나타내는 CRect 클래스, 2차원좌표를나타내는 CPoint, 32 비트정수원소들에대한동적크기의배열구조를나타내는 CDWordArray, 메모장기능을지원하는 CView의자식클래스 CEditView 등이예제프로그램들에서사용된다. CxxxEx 클래스는 Cxxx 클래스에약간의기능이추가된 Cxxx 클래스의자식클래스이다. 예를들면, CDialog 클래스의자식클래스인 CDialogEx는 CDialog의멤버함수들외에도 GetThisClass(), PreTranslateMessage(), SetBackgroundColor(), SetBackgroundImage() 등의추가적인멤버함수들을갖고있다
70 3.2 MFC 프로그램의작성, 컴파일, 실행 먼저간단한 MFC 예제프로그램을 Visual C++ 에서작성, 컴파일, 실행등의작업을수행하는방법을보인다음예제프로그램을설명할것이다. 예제프로그램소스코드 //hello.cpp #include <afxwin.h> // Declare the application class class CHelloApp : public CWinApp { public: virtual BOOL InitInstance(); ; // Create an instance of the application class CHelloApp HelloApp; // Declare the main window class class CHelloWindow : public CFrameWnd { CStatic* cs; public: CHelloWindow(); ; // The InitInstance function is called each // time the application first executes. BOOL CHelloApp::InitInstance() { m_pmainwnd = new CHelloWindow(); m_pmainwnd->showwindow(m_ncmdshow); m_pmainwnd->updatewindow(); return TRUE; // The constructor for the window class CHelloWindow::CHelloWindow() { // Create the window itself Create(NULL, _T("Hello World!"), WS_OVERLAPPEDWINDOW, CRect(0, 0, 300, 200)); // Create a static label cs = new CStatic(); cs->create(_t("hello world"), WS_CHILD WS_VISIBLE SS_CENTER, CRect(50, 80, 250, 150), this); 이프로그램은다음과같은세가지작업을수행한다. 1 응용프로그램객체 를생성한다. 모든 MFC 프로그램은 MFC 및윈도우운영체제관련초기화작업을수행하는한개의응용프로그램객체를갖는다. 2 응용프로그램객체는화면상에창을하나만드는데, 이창은응용프로그램의주실행창이된다. 3 주실행창안에 "hello world" 라는내용을담고있는텍스트레이블컨트롤을생성한다. 위의예제프로그램을입력하고컴파일하는일은 1장에서텍스트기반프로그램을처리하는일과크게다르지않으며, 다음과같이진행된다
71 (1) [ 파일 ] 메뉴 -> [ 새로만들기 ] - [ 프로젝트 ] 메뉴항목을선택하여아래화면이나타나면, Win32 - Win32 프로젝트유형을선택하고프로젝트이름을입력한후, < 확인 > 버튼을누른다. 아래화면에서 < 빈프로젝트 > 체크박스를선택하고, < 마침 > 버튼을누른다
72 C++ 소스프로그램의입력을위한과정은 1 장의경우와같다
73 (2) 예제프로그램을입력한후 [ 디버깅하지않고시작 ] 메뉴항목등의선택을통해실행프로그램을빌드하는과정의링크단계에서다음과같은오류메시지를얻게될것이다. 1>c: program files microsoft visual studio 10.0 vc atlmfc include afx.h(24): fatal error C1189: #error : Building MFC application with /MD[d] (CRT dll version) requires MFC shared dll version. Please #define _AFXDLL or do not use /MD[d] ========== 빌드 : 성공 0, 실패 1, 최신 0, 생략 0 ========== (3) 위의오류메시지가나타내는것은이프로그램이 MFC 라이브러리를사용하고있는데, 컴파일러옵션이이를반영하고있지않은때문에발생한것이다. 그이유는 Win32 프로젝트유형에서는 MFC 라이브러리를사용하지않는것이디폴트옵션이기때문이다. 따라서컴파일전에 MFC 라이브러리관련옵션을변경해주어야한다. [ 프로젝트 ] 메뉴하단의 [MFCTest( 프로젝트이름 ) 속성 ] 메뉴항목을선택하면아래그림과같은다이얼로그박스가나타날것이다. 왼쪽열은 < 구성속성 > - < 일반 > 으로선택되어있을때, 가운데열의프로젝트기본값중두번째항목인 <MFC 사용 > 의값이 표준 Windows 라이브러리사용 으로되어있을것이다. <MFC 사용 > 항목의오른쪽끝의콤보박스버튼을누르면, 아래와같은리스트가나타날것이다. 이리스트에는 표준 Windows 라이브러리사용 이외에도 정적라이브러리에서
74 MFC 사용, 공유 DLL에서 MFC 사용 등의항목들이있다. 이들중어느것을선택해도무방하지만, 가장아래항목인 공유 DLL에서 MFC 사용 을선택한다. 이런후에다시 [ 디버깅하지않고시작 ] 메뉴항목을선택하면컴파일과실행이이루어지게될것이다. 프로그램이실행되면전체화면의좌측상단에오른쪽그림과같은주실행창이나타날것이다. 이프로그램에대해주실행창의크기변경, 위치변경, 종료등의사용자작업을수행할수있다. 참고사항 1 [Project -> Settings -> General] 에서 Microsoft Foundation Classes: 라는텍스트레이블아래의콤보박스버튼으로선택할수있는세개의항목은다음과같다. 표준 Windows 라이브러리사용 정적라이브러리에서 MFC 사용 공유 DLL에서 MFC 사용 첫번째옵션은 Win32 프로젝트유형에서는디폴트이며, 물론 MFC를사용하지않을것임을나타낸다. 나머지두옵션은 MFC 라이브러리를사용하되사용하는방식을다르게지정하고있다. 정적라이브러리방식으로 MFC를사용하겠다는두번째옵션은컴파일과정의링크단계에서프로그램에서사용하고있는라이브러리코드들을실행파일에포함시킨다는뜻이다. 반면에마지막옵션인동적링크라이브러리 (Dynamic Link Library; DLL) 방식으로 MFC를사용한다는것은링크단계에서실행파일을만들때라이브러리코드를포함시키는대신실행중에동적링크를할수있는준비만해둔다. 동적링크라이브러리방식의경우정적라이브러리방식에비해실행파일의크기가훨씬작은장점이있다. 따라서실행시메모리에읽어들일파일의크기도작으므로메모리사용량측면에서도유리하며읽어들이는시간도적게걸린다. 동적링크를위해사용되는라이브러리파일은 mfc??.dll이라는이름을갖는데, C: WINDOWS SYSTEM32 폴더에서이파일들을찾을수있다. 파일이름의?? 는버전을나타내며, Visual C++ 6.0의경우 mfc42.dll 파일이며 Visual C 의경우 mfc100.dll이다. 이파일에는개별프로그램이필요로하는라
75 이브러리코드만있는것이아니라전체 MFC 라이브러리코드를다포함하고있어큰파일이다. 그러나이파일을필요로하는첫번째프로그램에의해메모리에올라와있으면다른프로그램들은동일한 DLL 파일을공유할수있어추가로읽어들이는작업은필요없다. 이와같은이유에서 Shared( 공유 ) DLL이라는표현을사용한다. DLL 방식의단점은다른컴퓨터로옮겨실행할때, 필요한 DLL 파일이그컴퓨터에갖추어져있지않을경우프로그램을실행할수없다는점이다. 이러한문제를피하고싶을경우라면여러단점에도불구하고정적라이브러리방식의링크가사용되어야할것이다. 참고사항 2 컴파일작업의결과로만들어지는파일들은프로젝트폴더안의 Debug 폴더들과 ipch 폴더안에저장된다. 여기에는물론.exe 확장자를갖는실행파일도포함된다. 여기에들어있는파일들은언제라도프로그램소스에서다시만들어낼수있으므로당장필요하지않을경우지움으로써디스크공간을절약할수있다. [ 특히과제제출시에는이들파일을반드시삭제한후제출하여야한다.] 참고사항 3 컴파일오류가있을경우, 오류메시지가 Visual C++ 화면아래쪽의출력윈도우에나타낸다. 해당메시지를클릭하면그메시지에대응되는소스위치를찾아준다. 이기능은링크단계의오류의경우에는사용할수없다. 링크단계오류는대체로다음과같은경우자주발생한다. MFC 사용옵션이선택되어있지않다. ( 참고사항 1 참고 ) 함수호출시사용하는이름에철자오류가포함되어정의되어있는함수이름과정확히일치하지않는다. 이전에실행된프로그램을종료하지않은상태에서프로그램을수정한후새로컴파일한다. 이경우이전의실행파일을지울수없어새로운실행파일을만들수없으며, 다음과같은오류메시지를보게될것이다. 1> 빌드시작 : 프로젝트 : MFCTest, 구성 : Debug Win > Hello.cpp 1> _WIN32_WINNT not defined. Defaulting to _WIN32_WINNT_MAXVER (see WinSDKVer.h) 1>LINK : fatal error LNK1168: D: class Projects MFCTest Debug MFCTest.exe을 ( 를 ) 쓰기용으로열수없습니다. ========== 빌드 : 성공 0, 실패 1, 최신 0, 생략 0 ========== 3.3 MFC 프로그램예제해설 이절에서는앞절에서사용된 MFC 예제프로그램의전체구조와각부분의내용을차례로
76 살펴본다. 프로그램설계 여기에서작성하려는프로그램은창안에 "hello world" 라는메시지를출력하는프로그램이다. 이러한응용프로그램은먼저화면상에창을생성하는데, 그창의타이틀바에 "Hello World!" 라는제목을넣는다. 그다음창안에 "hello world" 라는텍스트를표시한다. 이러한작업을위해다음과같은 3개의객체가필요하다. 응용프로그램을초기화하고윈도우운영체제에연결하는작업을수행하는응용프로그램객체가필요하며, 이객체는또한이벤트처리작업을담당한다. 응용프로그램의주실행창의역할을담당하는창객체. "hello world" 라는텍스트레이블을보여주는텍스트컨트롤객체. 위의객체들중첫두객체는모든 MFC 프로그램에공통적으로포함되는객체들이다. 세번째객체는여기서의예제응용프로그램에서필요하여포함시킨객체이다. 개별응용프로그램마다사용자로부터의입력을받기위해또는출력을표현하기위한여러가지사용자인터페이스객체들을가질수있다. 대체적인사용자인터페이스설계가완료되면그와같은인터페이스를구현하기위해창이나컨트롤등을생성하기위한코드를작성하게될것이다. 이프로그램의경우에는텍스트레이블을나타내는컨트롤하나만사용하고있지만주실행창이나여러다이얼로그박스들에서수백개의컨트롤들을사용할수도있다. 예제코드이해하기 아래에보여주고있는코드는앞의예제코드에설명의편의를위해행번호를붙인것이다. 1 //hello.cpp 2 #include <afxwin.h> 3 // Declare the application class 4 class CHelloApp : public CWinApp 5 { 6 public: 7 virtual BOOL InitInstance(); 8 ;
77 9 // Create an instance of the application class 10 CHelloApp HelloApp; 11 // Declare the main window class 12 class CHelloWindow : public CFrameWnd 13 { 14 CStatic* cs; 15 public: 16 CHelloWindow(); 17 ; 18 // The InitInstance function is called each 19 // time the application first executes. 20 BOOL CHelloApp::InitInstance() 21 { 22 m_pmainwnd = new CHelloWindow(); 23 m_pmainwnd->showwindow(m_ncmdshow); 24 m_pmainwnd->updatewindow(); 25 return TRUE; // The constructor for the window class 28 CHelloWindow::CHelloWindow() 29 { 30 // Create the window itself 31 Create(NULL, 32 -T("Hello World!"), 33 WS_OVERLAPPEDWINDOW, 34 CRect(0, 0, 300, 200)); 35 // Create a static label 36 cs = new CStatic(); 37 cs->create(_t("hello world"), 38 WS_CHILD WS_VISIBLE SS_CENTER, 39 CRect(50, 80, 250, 150), 40 this); 41 위의프로그램은 6 개의부분으로나누어볼수있다
78 [2] 헤더파일 afxwin.h에대한 include를나타내고있는데, 이파일은 MFC에서사용되는타입정의, 클래스선언, 변수정의등을포함하고있으며, 윈도우 API 라이브러리사용을위한다른헤더파일들에대한 include들도포함하고있다. [3-8] MFC에서표준응용프로그램을나타내는 CWinApp 클래스를상속받아정의되는응용프로그램클래스 CHelloApp가선언되고있다. 이클래스를선언하는이유는 CWinApp 클래스에서상속받은가상함수 InitInstance() 를재정의하기위해서이며, 이함수는응용프로그램실행이시작될때호출된다. [10] 응용프로그램객체 HelloApp를전역변수로정의한다. 이프로그램의실행이시작되는곳이바로이전역변수의생성이다. 응용프로그램이메모리에읽혀들어가서실행이시작되면, 전역변수가생성되며 CHelloApp 클래스의기본생성자함수가실행되는데, 프로그램에서이함수를정의하고있지않으므로컴파일러가제공하는기본생성자를사용할것이다. 컴파일러가제공하는기본생성자는현재클래스에서정의되는멤버변수들에대한생성자호출과함께부모클래스인 CWinApp의기본생성자함수를호출하게된다. CWinApp의생성자함수는 InitInstance() 함수를호출하도록되어있는데, 이함수는가상함수이므로 CWinApp 클래스의 InitInstance() 함수가아니라 [18-26] 에정의되어있는 CHelloApp 클래스의 InitInstance() 함수를호출하게된다. [11-17] MFC 클래스인 CFrameWnd 클래스를상속받아정의되는 CHelloWindow 클래스가선언되고있다. 이클래스의인스턴스가이응용프로그램의주실행창의역할을한다. 새로운클래스를선언한것은생성자, 소멸자, 멤버변수등을구현할수있도록하기위함이다. [18-26] CHelloApp::InitInstance() 함수가구현되어있다. 이함수의역할은주실행창의생성을위해 CHelloWindow 클래스의인스턴스를생성하는일이며, 그결과로 [27-41] 에정의된생성자함수 CHelloWindow() 가실행된다. [27-41] 생성자함수 CHelloWindow() 가구현되어있으며, 이함수는창을생성한다음그내부에텍스트컨트롤을생성한다. 이프로그램에서유의할점은 C/C++ 프로그램의실행시작위치를나타내는 main() 함수도없고, 또예전윈도우응용프로그램의시작위치를나타내던 WinMain() 함수도없다는점이다. 그리고 WinMain() 함수의주요내용이었던이벤트처리루프도보이지않는다. 이러한내용들은모두 CWinApp 클래스에구현되어숨겨져있다. 또창의최대화, 최소화, 이동등을수행할수있는데, 이를위한코드들은 CFrameWnd 클래스안에숨겨져있다. 아래에서는위의프로그램을구성하는 3 개의객체를중심으로더욱자세히살펴본다
79 응용프로그램객체 모든 MFC 프로그램은 CWinApp 클래스에서파생된응용프로그램객체한개를갖는다. 이객체는반드시전역변수로선언되어야한다. CWinApp 클래스에서파생된객체는주실행창의생성을포함하는응용프로그램의초기화를수행하며, 이벤트처리루프를갖는다. 주실행창은응용프로그램마다다르며, 따라서이를위한코드작성이필요한데, 이를위해재정의되는함수가가상함수 InitInstance() 이다. MFC 프로그램에서 CWinApp 객체를사용하지못하고그파생클래스를사용하는이유가여기에있다. [3-8] 의코드가파생클래스 CHelloApp를선언하고있는데, CWinApp 클래스를상속받는다는사실과 InitInstance() 함수를재정의할것임을선언하고있다. 3 // Declare the application class 4 class CHelloApp : public CWinApp 5 { 6 public: 7 virtual BOOL InitInstance(); 8 ; [18-26] 에구현되어있는 InitInstance() 함수는 CHelloApp의데이터멤버의하나이며주실행창객체에대한포인터로사용되는변수 m_pmainwnd를사용하여창을생성하여화면에표시한다. CWinApp는 m_pmainwnd 외에도여러다른데이터멤버들을가지고있는데, 예를들면 m_pszappname( 응용프로그램이름 ), m_lpcmdline( 응용프로그램실행을위한명령 ), m_ncmdshow( 주실행창의초기모습 ) 등인데, 이변수들은응용프로그램객체가생성되면서프로그램등록정보로부터받아온적절한값으로초기화된다. 18 // The InitInstance function is called each 19 // time the application first executes. 20 BOOL CHelloApp::InitInstance() 21 { 22 m_pmainwnd = new CHelloWindow(); 23 m_pmainwnd->showwindow(m_ncmdshow); 24 m_pmainwnd->updatewindow(); 25 return TRUE; 26 25번행에서 TRUE를리턴하는것은초기화가성공적으로완료되었음을의미한다. 만일여기에서 FALSE를리턴한다면프로그램은즉시종료된다
80 창객체 MFC는주실행창으로사용될수있는창유형으로크기변경이가능한프레임윈도우와크기가고정된다이얼로그윈도우등두가지를정의하고있다. 이예제에서는프레임윈도우를사용하고있다. 아래코드는 CFrameWnd 클래스의파생클래스 CHelloWindow를선언하고있다. 11 // Declare the main window class 12 class CHelloWindow : public CFrameWnd 13 { 14 CStatic* cs; 15 public: 16 CHelloWindow(); 17 ; 파생클래스에는새로운생성자함수와컨트롤객체에대한포인터변수가포함되어있다. 응용프로그램마다주실행창안에각자고유한컨트롤들을가질것이며, 이를위해새로운생성자함수를필요로한다. 이클래스에서는정의되어있지않지만, 생성자함수에서생성된객체들을삭제하기위한소멸자함수를정의할수도있다. 그러나주실행창이소멸되는상황이라면일반적으로전체프로그램이종료될것이므로동적으로할당된객체들을따로삭제할필요는없다. 그리고창자체나창안의컨트롤들을통해발생된이벤트들을처리하는함수도이클래스안에표시될수있으며, 5절에서그러한예들을살펴볼것이다. 대부분의윈도우응용프로그램들은한개씩의주실행창을갖는다. 따라서 CHelloApp 클래스도주실행창을가리키는데이터멤버 m_pmainwnd를갖고있는데, 이변수는 CWinThread에서상속된데이터멤버이다. 주실행창의생성을위해 InitInstance() 함수는 22 번행에서와같이 CHelloWindow 클래스의인스턴스를생성하고 m_pmainwnd 변수로하여금그창객체를가리키게만든다. 18 // The InitInstance function is called each 19 // time the application first executes. 20 BOOL CHelloApp::InitInstance() 21 { 22 m_pmainwnd = new CHelloWindow(); 23 m_pmainwnd->showwindow(m_ncmdshow); 24 m_pmainwnd->updatewindow(); 25 return TRUE;
81 창객체의생성으로끝나는것이아니라새로운창을화면에적절히나타나게만들기위해두가지작업이더필요하다. 창객체에대해 ShowWindow() 함수를호출하여창자체를화면에나타나게하는작업, 그리고 UpdateWindow() 함수를호출하여창의클라이언트영역의내용물을새로그리게함으로써창안의컨트롤등이정확히나타나게만드는작업이다. 이함수들은 CFrameWnd의부모클래스인 CWnd 클래스의멤버함수들이다. (CWnd는 200개가넘는멤버함수들을갖고있는방대한클래스이다.) CWnd::ShowWindow() 함수의파라미터는창을어떤모습으로나타나게할것인지를지정하는데, SW_HIDE( 창감춤 ), SW_RESTORE( 원래크기복원 ), SW_SHOWMAXIMIZED( 최대크기 ), SW_SHOWMINIMIZED( 최소화, 작업표시줄아이콘 ), SW_SHOWNORMAL( 정상크기 ) 등의여러가지값을사용할수있다. 여기에서는프로그램등록정보로부터초기화된 m_ncmdshow를사용하고있다. 22번행에서는 new 연산자를사용하여 CHelloWindow 클래스인스턴스를생성하고있으며, 이클래스의생성자인 CHelloWindow() 가호출된다. 이생성자는먼저 CFrameWnd 클래스의멤버함수 Create() 를호출하여프레임윈도우를생성한다. 27 // The constructor for the window class 28 CHelloWindow::CHelloWindow() 29 { 30 // Create the window itself 31 Create(NULL, 32 _T("Hello World!"), 33 WS_OVERLAPPEDWINDOW, 34 CRect(0, 0, 300, 200)); Create() 함수에는 4개의파라미터가전달된다. 첫번째파라미터 NULL은디폴트클래스인 CWnd에서정의된속성구조가사용될것임을표시한다. 두번째파라미터는창의타이틀바에나타날텍스트이다. 세번째파라미터는창의스타일속성을나타내는값인데, 이창은다른 ( 응용프로그램의 ) 창에의해가려질수있는정상적인창임을나타낸다. 네번째파라미터는이창의위치와크기를나타내기위해사각영역을정의하는 CRect 클래스의생성자를사용하고있다. 처음두값 (0, 0) 은사각영역의좌측상단의좌표, 마지막두값 (300, 200) 은사각영역의우측하단의좌표를나타낸다. 이는이창이전체화면의좌측상단에위치하며, 가로세로의크기가각기 300, 200임을의미한다. 텍스트컨트롤 CHelloWindow() 는프레임윈도우의생성에이어텍스트컨트롤객체를생성한다. 이객체를가리키기위한 CStatic*, 타입의변수는 CHelloWindow 클래스에서 private 데이터멤버로선언되어있다. 텍스트컨트롤객체를만드는작업은두단계로이루어진다. 먼저 CStatic 클래스의생성자
82 를호출하여필요한객체를생성한다음, CStatic::Create() 함수를호출하여그세부사항을정한다. 27 // The constructor for the window class 28 CHelloWindow::CHelloWindow() 29 { 30 // Create the window itself 31 Create(NULL, _T("Hello World!"), 33 WS_OVERLAPPEDWINDOW, 34 CRect(0, 0, 200, 200)); 35 // Create a static label 36 cs = new CStatic(); 37 cs->create(_t("hello world"), 38 WS_CHILD WS_VISIBLE SS_CENTER, 39 CRect(50, 80, 250, 150), 40 this); CStatic::Create() 함수를위한파라미터들은 CWnd::Create() 함수의경우와유사하지만약간의차이가있다. 첫번째파라미터는텍스트컨트롤이보여주는텍스트를나타내는스트링이다. 두번째파라미터는스타일속성들을나타낸다. WS_CHILD 스타일은이컨트롤이나타내는창은다른창내부에위치하며, 독자적으로존재하는것이아니라그창과함께이동하는창임을나타낸다. 이러한성격의창을자식창이라고하며, 이창을포함하는창은부모창이라고한다. WS_VISIBLE은이창이감추어져있지않고화면에보인다는것을나타낸다. SS_CENTER는이컨트롤의 ( 수평방향으로 ) 중앙의위치에텍스트가표시될것임을나타낸다. 세번째파라미터는텍스트컨트롤의위치와크기를나타낸다. 이컨트롤은주실행창의타이틀바부분과테두리선을제외한클라이언트영역이라불리는창내부에나타날수있으며, 따라서좌표값의기준위치도클라이언트영역의좌측상단이된다. 네번째파라미터는이컨트롤을자식창으로갖는부모창을가리키는포인터인데, 현재 CHelloWindow 클래스안이므로 this는 CHelloWindow 인스턴스인주실행창을가리킨다. 3.4 MFC 스타일 컨트롤은윈도우응용프로그램들을위한인터페이스역할에이용되는사용자인터페이스요소들이다. 많은응용프로그램에나타나는윈도우나다이얼로그박스들에서프로그램기능을적절히표현하고있는컨트롤들이주된요소들이다. 따라서윈도우운영체제들이제공하고있는컨트롤의용법을이해하는것은대단히중요한일이다. 컨트롤들은무엇을할수있는가, 컨트롤의외관이나동작의세부사항을어떻게변경할수있는가, 사용자의이벤트에대해컨
83 트롤이적절히반응하도록만들기위해서필요한작업은무엇인가등의내용들에대해이해할필요가있다. 이러한지식과다이얼로그나메뉴등의용법에관한지식을활용하여다양한윈도우응용프로그램을만들수있다. 표준컨트롤들을표현하기위한클래스에는 CStatic, CButton, CEdit, CList, CComboBox, CScrollBar 등의 6개가있다. 이들중에가장용법이단순한클래스는내용이고정된텍스트를출력하는 CStatic이다. CStatic 클래스는데이터멤버를갖지않으며, 멤버함수도생성자와 Create() 함수, 그리고텍스트대신아이콘, 비트맵, 커서이미지등의그림을처리하기위한몇개의함수정도이다. 이절에서는컨트롤의외관과동작을조정하기위한스타일의설명을위해 CStatic 클래스의예를살펴보며, 다음절에서는 CButton 클래스와 CScrollBar 클래스등을다룬다. 기초사항 CStatic 클래스는사용자에게내용이고정된텍스트메시지를보여주는것이주된용도이며, 이러한텍스트는어떤정보를전달하기위한메시지일수도있고, 다른컨트롤을설명하는레이블일수도있다. CStatic 객체들은텍스트외에사각영역, 사각형프레임, 아이콘, 비트맵등의다른모습으로나타날수도있다. 사각영역이나사각형프레임은연관성있는다른컨트롤들을그룹으로묶어보여주거나분리해서보여주기위해사용될수있다. 이러한외관을정해줄때사용하는것이스타일이다. CStatic 컨트롤은항상다른창의자식창형태로나타난다. 3절에서살펴본예제에서텍스트컨트롤을만들기위한 2 단계코드는다음과같다. CStatic *cs;... cs = new CStatic(); cs->create(_t("hello world"), WS_CHILD WS_VISIBLE SS_CENTER, CRect(50, 80, 150, 150), this); 생성자와 Create() 함수를차례로호출하는 2 단계작업은대부분의컨트롤들에서일반적으로사용되는절차이다. 생성자는객체표현을위한메모리를할당하고초기화작업을수행하며, Create() 함수는컨트롤창을만들어화면에나타나도록만드는역할을수행한다. 아래와같은프로토타입을갖는 Create() 함수는다섯개의파라미터를갖지만마지막파라미터는디폴트인수를갖고있으며, CStatic 컨트롤의경우보통다른값을지정하지않는다. BOOL Create(LPCTSTR lpsztext, DWORD dwstyle, const RECT& rect, CWnd* pparentwnd, UINT nid = 0xffff);
84 여기서설명의초점이되는파라미터는 32비트정수를나타내는 DWORD 타입의 dwstyle이며, 컨트롤의스타일정보를표현한다. CStatic 스타일 컨트롤들은여러가지스타일을갖는데, 이값은 Create() 함수에게전달되는 dwstyle 파라미터에의해정해진다. 각스타일속성은 32 비트중어느한비트를이용하여표시된다. 여러속성을결합하기위해서는비트단위 OR 연산자 를사용한다. 예를들면, 위에서사용된스타일속성및이들을결합한결과는아래와같다. WS_CHILD: WS_VISIBLE: SS_CENTER: WS_CHILD WS_VISIBLE SS_CENETR: 텍스트컨트롤은일종의창이며, CStatic 클래스는 CWnd 클래스의자식클래스이다. 따라서 CStatic 클래스에고유하게적용되는스타일도있고 CWnd 클래스에적용되는스타일중 CStatic 클래스에사용될수있는것들도있다. CWnd 클래스에서상속받은스타일 WS_CHILD: 다른창의자식창임을나타내며, CStatic에서는필수스타일 WS_VISIBLE: 화면에보임을나타내며, 대개의경우사용됨. WS_DISABLED: 사용자로부터의이벤트에반응하지않음을나타내는데, 텍스트컨트롤의경우원래반응하지않으므로대체로불필요함. WS_BORDER: 컨트롤에경계선이그려짐. "WS" 는 Window Style 을나타낸다. CStatic 에고유한스타일 아래스타일상수에서 "SS" 는 Static Style 을나타낸다. 텍스트위치관련스타일 SS_CENTER: 텍스트를가운데정렬하여표시한다. 필요하면여러행을사용한다
85 SS_LEFT: 텍스트를왼쪽정렬하여표시한다. 필요하면여러행을사용한다. SS_RIGHT: 텍스트를오른쪽정렬하여표시한다. 필요하면여러행을사용한다. SS_LEFTNOWORDWRAP: 텍스트를왼쪽정렬하여표시한다. 한줄에들어가지않는부분은표시하지않는다. 사각영역및프레임표시스타일 아래의스타일중의하나가사용될경우 Create() 함수의첫번째파라미터로주어지는텍스트스트링은무시된다. SS_BLACKFRAME: 윈도우프레임의배경색과같은프레임을그린다. 디폴트색상은 BLACK이다. SS_BLACKRECT: 윈도우프레임의배경색과같은사각영역을그린다. 디폴트색상은 BLACK이다. SS_GRAYFRAME: 바탕화면의배경색과같은프레임을그린다. 디폴트색상은 GRAY 이다. SS_GRAYRECT: 바탕화면의배경색과같은사각영역을그린다. 디폴트색상은 GRAY 이다. SS_WHITEFRAME: 일반창의배경색과같은프레임을그린다. 디폴트색상은 WHITE이다. SS_WHITERECT: 일반창의배경색과같은사각영역을그린다. 디폴트색상은 WHITE 이다. 기타 : SS_SIMPLE, SS_NOPREFIX, SS_USERITEM, SS_ICON, SS_BITMAP, SS_CENTERIMAGE CStatic 객체에서 WS_CHILD와 WS_VISIBLE은거의항상나타난다. WS_DISABLED을사용하는것은대부분의경우의미가없다. 그외의모든속성은레이블의외관에영향을미치는선택항목이다. CStatic 텍스트외관 아래코드는 3 절의소스코드에서생성자함수 CHelloWindow() 만조금고친것이다. CHelloWindow::CHelloWindow() { // Create the window itself Create(NULL, _T("CStatic Tests"), WS_OVERLAPPEDWINDOW, CRect(0, 0, 300, 200)); // Get the size of the client rectangle
86 CRect r; GetClientRect(&r); r.inflaterect(-40, -20); // Create a static label cs = new CStatic(); cs->create(_t("hello world"), WS_CHILD WS_VISIBLE WS_BORDER SS_CENTER, r, this); 이코드에서의주요변화는텍스트컨트롤의위치와크기를나타내기위해 CRect(50, 80, 250, 150) 을사용하는대신, 주실행창클라이언트영역안에상하좌우각기 20 픽셀씩의여백을갖는사각영역을계산하여사용하는것이다. 이를위해 CRect 타입의변수 r을선언한다음, CHelloWindow::GetClientRect(&r) 함수를호출하고있다. GetClientRect() 함수는원래 CWnd 클래스에서상속된것인데, 창의클라이언트영역을나타내는좌표들을 CRect 객체에받아오기위해사용된다. CRect::InflateRect() 함수는 2개의정수파라미터를취하는데, 각기좌우및상하방향으로사각영역을확대하기위해사용된다. 여기서는실인수로각기 -20을사용하였으므로확대대신축소된다 (CRect::DeflateRect() 함수참고 ). 또다른변화는 WS_BORDER 스타일을 CStatic::Create() 함수의스타일파라미터로추가한것이다. 이프로그램의실행결과를오른쪽에보인다. cs->create() 함수에서한줄에표시될수없는긴스트링을사용한예제와실행결과는다음과같다. cs->create(_t("now is the time for all good men to come to the aid of their country"), WS_CHILD WS_VISIBLE WS_BORDER SS_CENTER, r, this); SS_LEFTNOWORDWRAP 의효과를보이기위한예제와실행결과는다음과같다
87 cs->create(_t("now is the time for all good men to come to the aid of their country"), WS_CHILD WS_VISIBLE WS_BORDER SS_LEFTNOWORDWRAP, r, this); SS_BLACKFRAME의효과를보이기위한예제와실행결과는다음과같다. cs->create(_t("hello world"), WS_CHILD WS_VISIBLE SS_BLACKFRAME, r, this); SS_GRAYRECT의효과를보이기위한예제와실행결과는다음과같다. cs->create(_t("hello world"), WS_CHILD WS_VISIBLE SS_GRAYRECT, r, this); 폰트 CStatic 객체에서사용되는폰트를변경하기위해서는 CFont 객체를이용한다. CStatic 객체를먼저생성한후, CFont 객체를생성한다. CFont::CreateFont() 함수를사용하여폰트를정한다음, CWnd::SetFont() 함수를사용하여새로정해진폰트를 CStatic 객체와연결해준다. CreateFont() 함수의프로토타입은아래와같다. BOOL CreateFont(int nheight, int nwidth, int nescapement, int norientation, int nweight, BYTE bitalic, BYTE bunderline, BYTE cstrikeout, BYTE ncharset, BYTE noutprecision, BYTE nclipprecision, BYTE nquality, BYTE npitchandfamily, LPCTSTR lpszfacename); 이중자주사용되는파라미터 3개만살펴보자. nheight = 36은포인트단위의글자크기를나타내는데, 10 포인트가일반텍스트용글자크기이며, 이책의본문글자크기는 11 포인트를사용하고있다. nweight = 700은글자의굵기를나타내는데, 400이면보통굵기이며 700 이면진한글씨체 (boldface) 이다. lpszfacename은윈도우운영체제에서사용될수있는글꼴의종류를나타내는데, Arial, Courier New, Symbol, Times New Roman 등등의수많은글꼴들이있다
88 CHelloWindow::CHelloWindow() { CRect r; // Create the window itself Create(NULL, _T("CStatic Tests"), WS_OVERLAPPEDWINDOW, CRect(0, 0, 300, 200)); // Get the size of the client rectangle GetClientRect(&r); r.inflaterect(-40, -20); // Create a static label cs = new CStatic(); cs->create(_t("hello World"), WS_CHILD WS_VISIBLE WS_BORDER SS_CENTER, r, this); // Create a new 36 point Arial font CFont *font = new CFont(); font->createfont(36, 0, 0, 0, 700, 0, 0, 0, ANSI_CHARSET,OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH FF_DONTCARE, _T("Arial")); // Cause the label to use the new font cs->setfont(font); 3.5 메시지맵 (message map) 응용프로그램에서창안에배치한사용자인터페이스요소는두가지제어가능한특성을갖는데, 이들은외관과이벤트반응동작이다. 4절에서 CStatic 컨트롤의예를통해사용자인터페이스요소의외관을조정하기위해스타일속성을이용한다는것을살펴보았다. 이러한방식은 MFC 안의모든컨트롤클래스들에적용된다. 이절에서는메시지맵과간단한이벤트처리의이해에도움을주기위해먼저 CButton 컨트롤을살펴본다. 또컨트롤이아니라창자체에서발생하는이벤트들에대한처리방법도알아본다. 그런다음조금더복잡한예로 CScrollBar 컨트롤의경우를알아볼것이다
89 메시지맵이해하기 3절에서알아본것처럼 MFC 프로그램들에서 main() 함수나이벤트루프가드러나있지않다. 모든이벤트처리가 CWinApp 클래스안에숨겨진 C++ 코드들에의해수행된다. 운영체제로부터전달되어온이벤트들의큐에서이벤트들을하나씩차례로꺼내살펴보는이벤트루프가숨겨져있기때문에, 이벤트루프에서응용프로그램에의미있는이벤트들이나타났을때이벤트루프가이사실을프로그램에전달할수있는방법이필요하며, 바로메시지맵이이를위한메커니즘이다. 메시지맵은어떤이벤트가프로그램에의미있는이벤트인지, 또이러한이벤트들에반응하여호출될함수가무엇인지등을표시한다. 예를들어사용자가 "Quit" 이라는레이블을갖는버튼을누를때마다종료되는프로그램을작성한다고하자. 이를위해먼저적절한레이블을갖는버튼을생성할것이다. 그다음에버튼컨트롤의부모창에해당하는클래스안에메시지맵을만든다. 이는사용자가버튼컨트롤을클릭할때마다버튼은그부모창을나타내는객체에메시지를전달하기때문이다. 부모창을위한메시지맵을만들어줌으로써버튼에서발생하는메시지를가로채어이용할수있는메커니즘이확립된다. 메시지맵은그버튼에특정이벤트가발생했을때 MFC로하여금특정함수를호출하게만든다. 이경우 "Quit" 버튼에대한클릭이의미있는이벤트이며, 이의처리를위해지정된함수안에응용프로그램종료를위한코드를넣어주어야할것이다. 메시지맵의작성과이벤트처리함수를정의하는것이외의나머지작업들은모두 MFC가처리해준다. 사용자가 "Quit" 버튼을클릭하면, 버튼경계선안쪽에점선모양이생기며버튼에강조효과가발생하며, 자동적으로 "Quit" 버튼클릭에연계된함수가호출되고프로그램이종료될것이다. 간단한코드의추가로이와같이사용자이벤트에반응할수있는프로그램이만들어진다. CButton 클래스 CStatic 컨트롤은사용자이벤트에반응하지않는유일한컨트롤이다. CStatic 컨트롤을아무리많이클릭하더라도묵묵부답의상태가유지될것이다. 그러나 CStatic 컨트롤을제외한나머지컨트롤들은모두사용자이벤트에대해두가지로반응한다. 첫째, 사용자가해당컨트롤을건드리면컨트롤의외관에약간의변화가생긴다. 둘째, 각컨트롤은프로그램이필요한반응을할수있도록코드에게메시지를보내고자시도한다. 이러한절차를알아보기위해먼저 CButton 컨트롤을포함하는간단한예제프로그램을살펴보자. 이프로그램은 CStatic 컨트롤대신 CButton 컨트롤을사용한다는점을제외하면 3절의예제와유사하다. 이프로그램은메시지맵을사용하지않고있으며따라서버튼이벤트처리함수도정의되어있지않다. 그러나버튼을클릭했을때외관변화는일어나며, 따라서컨트롤의기본적인외관변화는 MFC에의해자체적으로수행된다는것을알수있다. // button1.cpp #include <afxwin.h>
90 #define IDC_BUTTON 100 // Declare the application class class CButtonApp : public CWinApp { public: virtual BOOL InitInstance(); ; // Create an instance of the application class CButtonApp ButtonApp; // Declare the main window class class CButtonWindow : public CFrameWnd { CButton *button; public: CButtonWindow(); ; // The InitInstance function is called once // when the application first executes BOOL CButtonApp::InitInstance() { m_pmainwnd = new CButtonWindow(); m_pmainwnd->showwindow(m_ncmdshow); m_pmainwnd->updatewindow(); return TRUE; // The constructor for the window class CButtonWindow::CButtonWindow() { // Create the window itself Create(NULL, _T("CButton Tests"), WS_OVERLAPPEDWINDOW, CRect(0, 0, 300, 200)); // Get the size of the client rectangle CRect r; GetClientRect(&r); r.inflaterect(-20, -20);
91 // Create a button button = new CButton(); button->create(_t("push me"), WS_CHILD WS_VISIBLE BS_PUSHBUTTON, r, this, IDC_BUTTON); CButton 클래스의 Create() 함수에서다섯번째파라미터는해당버튼컨트롤에부여된고유번호를나타내는데, 문맥에따라컨트롤 ID 또는객체 ID 등으로불린다. CStatic의경우에도이파라미터가있지만디폴트인수가 -1로정해져있고쓰임새가없어잘사용하지않지만, 다른컨트롤들에서는메시지맵에서컨트롤을식별하는중요한역할을한다. 컨트롤 ID로는 100 이상의값을사용하며, 컨트롤마다다른번호를부여해야한다. 99 이하의값은시스템이리소스 ID 값으로예약해두고있다. 다른용도로이예제에서는숫자를직접사용하는대신 IDC_BUTTON이라는심볼상수를정의하여쓰고있다. 심볼앞부분의 "IDC_" 는컨트롤을위한 ID임을의미한다. CButton 클래스를위한스타일파라미터도 CStatic 클래스의경우와차이가있는데, BS_PUSHBUTTON( 일반버튼 ), BS_AUTORADIOBUTTON( 라디오버튼 ), BS_AUTOCHECKBOX ( 체크박스 ), BS_AUTO3STATE( 체크박스에부분체크상태추가 ), BS_LEFTTEXT 등을포함하는 11개의 "BS" ("Button Style") 스타일상수들이사용될수있다. 메시지맵의작성 아래의코드는앞의코드에버튼클릭처리를위한메시지맵과처리함수를추가한것이다. // button2.cpp #include <afxwin.h> #define IDC_BUTTON 100 // Declare the application class class CButtonApp : public CWinApp { public: virtual BOOL InitInstance(); ; // Create an instance of the application class CButtonApp ButtonApp;
92 // Declare the main window class class CButtonWindow : public CFrameWnd { CButton *button; public: CButtonWindow(); afx_msg void HandleButton(); DECLARE_MESSAGE_MAP() ; // The message handler function void CButtonWindow::HandleButton() { MessageBeep(-1); // The message map BEGIN_MESSAGE_MAP(CButtonWindow, CFrameWnd) ON_BN_CLICKED(IDC_BUTTON, HandleButton) END_MESSAGE_MAP() // The InitInstance function is called once // when the application first executes BOOL CButtonApp::InitInstance() { m_pmainwnd = new CButtonWindow(); m_pmainwnd->showwindow(m_ncmdshow); m_pmainwnd->updatewindow(); return TRUE; // The constructor for the window class CButtonWindow::CButtonWindow() { // Create the window itself Create(NULL, _T("CButton Tests"), WS_OVERLAPPEDWINDOW, CRect(0, 0, 300, 200)); // Get the size of the client rectangle CRect r; GetClientRect(&r); r.inflaterect(-20, -20);
93 // Create a button button = new CButton(); button->create(_t("push me"), WS_CHILD WS_VISIBLE BS_PUSHBUTTON, r, this, IDC_BUTTON); 위의코드에는이전의코드에다음과같은세가지사항이추가되어있다. CButtonWindow 클래스선언에는새로운멤버함수하나와 DECLARE_MESSAGE_MAP 마크로가포함되어있다. DECLARE_MESSAGE_MAP 마크로는이클래스를위한메시지맵이정의되어있음을표시한다. 멤버함수 HandleButton() 는앞에수식어 afx_msg를갖고있는데, 이함수가메시지처리기 ( 이벤트처리기, 이벤트처리함수 ) 임을나타낸다. 이함수와마크로모두 public으로선언되어야한다. HandleButton() 함수는일반멤버함수와동일한방식으로정의된다. 이함수안에는경고음을만들어내는윈도우 API 함수 MessageBeep() 의호출이들어있다. 이함수의파라미터로 -1( 표준경고음 ), MB_ICONASTERISK, MB_ICONEXCLAMATION, MB_ICONQUESTION, MB_ICONHAND, MB_OK 등을사용할수있다. 메시지맵을정의하는마크로들이들어있다. 메시지맵은 BEGIN_MESSAGE_MAP 마크로로시작하여되는데, END_MESSAGE_MAP 마크로로끝난다. BEGIN_MESSAGE_MAP 마크로는두개의파라미터를취한다. 첫번째파라미터는메시지맵이적용되는클래스이름을나타내는데, 해당메시지는이클래스의인스턴스인객체에게전달될것이며메시지를처리하는처리기함수도이클래스안에정의되어있다. 두번째파라미터는첫번째파라미터가나타내는클래스의부모클래스를나타내며, 현재의클래스에서처리되지않는부분은부모클래스로처리가넘겨진다. 이마크로들사이에 ON_BN_CLICKED 마크로가나타나는데, 이마크로는두개의파라미터를취한다. 첫번째마크로는메시지를발생시키는컨트롤의 ID를나타내며, 두번째파라미터는이메시지에대한처리기함수의이름을나타낸다. Visual Studio 도움말에서위의마크로들에대한형식과설명을찾을수있다. "ON_" 으로시작하는메시지마크로마다파라미터가다르며, 처리기함수의프로토타입이정해져있다. ON_BN_CLICKED 마크로의경우처리기함수는파라미터를취하지않으며, 리턴타입도 void로선언되어있다. 버튼을클릭하면명령 (COMMAND) 메시지가발생하여버튼창의부모창인주실행창에게전달된다. 명령메시지는메시지발생객체의리소스 ID를포함한다. 자식창들에서발생하는
94 메시지는원래부모창에게전달되도록되어있다. 부모창을나타내는 CButtonWindow 클래스에서는메시지맵을사용하여이메시지를처리할함수가 HandleButton() 임을결정할수있으며, 따라서버튼이클릭될때마다 HandleButton() 함수가실행되어경고음을내게된다. ON_BN_CLICKED 마크로이름중 "BN" 은 Button Notify( 버튼으로부터의통지 ) 를나타내며, CWnd 클래스의 ON_COMMAND 마크로와동등하며, 이에대한별칭으로생각해도좋다. WM_SIZE 메시지 위의예제에서는주실행창안의자식창인버튼에서발생된메시지를주실행창에서받아처리하였다. 창들은또한자신에게보내는메시지들을발생시키는데, 이러한메시지들은 Window Message를의미하는 "WM_" 으로시작한다. 이런메시지의종류는 100개도넘는다. 여기서는 WM_SIZE 메시지에대해알아보고, 다음에 WM_TIMER 메시지에대해알아본다. 프레임윈도우는크기가변경될수있으며, 응용프로그램중에는창의크기변화에따라창안의내용이조정되는것들이있다. 웹브라우저들은창의폭에맞추어텍스트를보여준다. 아이콘모음을담고있는창의경우폭에따라한줄에몇개의아이콘을배치할것인지결정한다. 또메모장에서는창의크기에따라창의우측이나하단에스크롤바가생기거나없어지기도한다. 이러한일이가능하기위해서는창의크기변화라는이벤트를나타내는메시지가필요하며, WM_SIZE가바로이를위한메시지이다. 이메시지는창의크기가변경될때마다발생한다. WM_SIZE 메시지처리를포함하는다음예제코드를살펴보자. 이예제에서는창의크기가변경됨에따라창안의버튼의크기도여백크기를유지하며함께변하고있다. // button3.cpp #include <afxwin.h> #define IDC_BUTTON 100 // Declare the application class class CButtonApp : public CWinApp { public: virtual BOOL InitInstance(); ; // Create an instance of the application class CButtonApp ButtonApp; // Declare the main window class class CButtonWindow : public CFrameWnd {
95 public: ; CButton *button; CButtonWindow(); afx_msg void HandleButton(); afx_msg void OnSize(UINT, int, int); DECLARE_MESSAGE_MAP() // A message handler function void CButtonWindow::HandleButton() { MessageBeep(-1); // A message handler function void CButtonWindow::OnSize(UINT ntype, int cx, int cy) { CRect r; GetClientRect(&r); r.inflaterect(-20, -20); button->movewindow(r); // The message map BEGIN_MESSAGE_MAP(CButtonWindow, CFrameWnd) ON_BN_CLICKED(IDC_BUTTON, HandleButton) ON_WM_SIZE() END_MESSAGE_MAP() // The InitInstance function is called once // when the application first executes BOOL CButtonApp::InitInstance() { m_pmainwnd = new CButtonWindow(); m_pmainwnd->showwindow(m_ncmdshow); m_pmainwnd->updatewindow(); return TRUE;
96 // The constructor for the window class CButtonWindow::CButtonWindow() { // Create the window itself Create(NULL, _T("CButton Tests"), WS_OVERLAPPEDWINDOW, CRect(0, 0, 300, 200)); // Get the size of the client rectangle CRect r; GetClientRect(&r); r.inflaterect(-20,-20); // Create a button button = new CButton(); button->create(_t("push me"), WS_CHILD WS_VISIBLE BS_PUSHBUTTON, r, this, IDC_BUTTON); 메시지맵안의 ON_WM_SIZE 마크로가주실행창에서발생하는 WM_SIZE 메시지처리를위한마크로이다. 주실행창은다른창의자식창이아니므로메시지를자신에게보낸다. 따라서이메시지는 CButtonWindow 클래스에서처리된다. ON_WM_SIZE 마크로는파라미터를갖지않음을유의하라. 이메시지를받은객체는 ID가주어지지않더라도어떤객체가이메시지를발생시켰는지이미알고있다. 바로자신이발생시켰기때문이다. 또 ON_BN_CLICKED의경우이메시지를발생시키는버튼이여러개있을수있고, 그에따라서로다른작업을하는처리함수이름을필요로한다. 그러나 WM_SIZE 메시지발생객체는하나뿐이므로아예처리함수의이름을 OnSize() 로고정시킴으로써이함수의역할을알아보기쉽게하고있다. CWnd 클래스에는 "On" 으로시작되는많은멤버함수들이있으며 OnSize() 함수와유사한역할을수행한다. 예를들면, WM_MOVE는창의이동시발생하는메시지이며, 이를위한메시지마크로는 ON_WM_MOVE이며, 처리기함수이름은 OnMove() 이다. 또창의일부또는전체가다른창에의해가려졌다가다시나타날경우발생하는메시지는 WM_PAINT, 관련마크로는 ON_WM_PAINT, 처리기함수는 OnPaint() 이다
97 OnSize() 함수의프로토타입은다음과같다. afx_msg void OnSize(UINT ntype, int cx, int cy); ntype는크기변경유형을나타내며, 이를위해정의된상수들에는 SIZE_MAXIMIZED, SIZE_MINIMIZED, SIZE_RESTORED, SIZE_MAXHIDE, SIZE_MAXSHOW 등이있으며, cx와 cy 는각기클라이언트영역의새로운폭과높이를나타낸다. OnSize(0 함수안에서버튼에적용되는 CWnd::MoveWindow() 함수는창의크기나위치를변경시킨다. WM_TIMER 메시지 이용방법이다소복잡한윈도우메시지들도있다. ON_WM_TIMER 메시지의경우메시지를발생시키기위해먼저 CWnd::SetTimer() 함수를호출해둔다. 이함수의첫번째파라미터는타이머를위한 ID이며, 두번째파라미터는 1/1000초단위로표시된타이머이벤트발생간격이다. 세번째파라미터가 NULL이면, WM_TIMER 메시지처리기함수이름은 OnTimer() 이지만, 별도의처리기함수이름을세번째파라미터를통해지정할수도있다. 아래의예제프로그램에서는 1초마다 WM_TIMER 메시지가발생하도록설정하였으며, 처리기함수 OnTimer() 에서는경고음을낸다. OnTimer() 함수의파라미터는타이머의 ID이다. 이메시지를 10번처리한후에는 OnTimer() 함수안에서 CWnd::KillTimer() 함수를호출하여타이머를종료시킨다. // button4.cpp #include <afxwin.h> #define IDC_BUTTON 100 #define IDT_TIMER1 200 // Declare the application class class CButtonApp : public CWinApp { public: virtual BOOL InitInstance(); ; // Create an instance of the application class CButtonApp ButtonApp;
98 // Declare the main window class class CButtonWindow : public CFrameWnd { CButton *button; public: CButtonWindow(); afx_msg void HandleButton(); afx_msg void OnSize(UINT, int, int); afx_msg void OnTimer(UINT); DECLARE_MESSAGE_MAP() ; // A message handler function void CButtonWindow::HandleButton() { MessageBeep(-1); // A message handler function void CButtonWindow::OnSize(UINT ntype, int cx, int cy) { CRect r; GetClientRect(&r); r.inflaterect(-20, -20); button->movewindow(r); // A message handler function void CButtonWindow::OnTimer(UINT id) { static int count = 10; MessageBeep(-1); if (--count == 0) KillTimer(id); // The message map BEGIN_MESSAGE_MAP(CButtonWindow, CFrameWnd) ON_BN_CLICKED(IDC_BUTTON, HandleButton) ON_WM_SIZE() ON_WM_TIMER() END_MESSAGE_MAP()
99 // The InitInstance function is called once // when the application first executes BOOL CButtonApp::InitInstance() { m_pmainwnd = new CButtonWindow(); m_pmainwnd->showwindow(m_ncmdshow); m_pmainwnd->updatewindow(); return TRUE; // The constructor for the window class CButtonWindow::CButtonWindow() { // Create the window itself Create(NULL, _T("CButton Tests"), WS_OVERLAPPEDWINDOW, CRect(0, 0, 300, 200)); // Set up the timer SetTimer(IDT_TIMER1, 1000, NULL); // 1000 ms. // Get the size of the client rectangle CRect r; GetClientRect(&r); r.inflaterect(-20, -20); // Create a button button = new CButton(); button->create(_t("push me"), WS_CHILD WS_VISIBLE BS_PUSHBUTTON, r, this, IDC_BUTTON); 스크롤바컨트롤 윈도우운영체제에서는스크롤바가표준스크롤바와스크롤바컨트롤이라는두가지형태로사용된다. 표준스크롤바는에디트컨트롤이나리스트박스컨트롤등의다른컨트롤이나창에부속되어있는경우를말한다. 다른컨트롤에부속되어있는경우스크롤바를소유하고있는
100 마스터컨트롤에스크롤바를처리하는기능이포함되어있으며이를위한별도의코딩은필요하지않다. 일반창의경우 WS_HSCROLL이나 WS_VSCROLL 등의스타일파라미터를사용하여스크롤바를만들어줄수있다. 스크롤바컨트롤은독립적으로존재하는스크롤바를말하며, 다른컨트롤들과마찬가지로컨트롤을만들어주고컨트롤에서발생한메시지처리를해주는등을위한코딩이필요하다. 아래예제코드에서는독립형수평스크롤바의생성과메시지맵을보여주고있다. 스크롤바이벤트에대해단지경고음만을내고있으므로스크롤박스 (scroll box 또는 thumb) 가적절히이동하지못한다. #include <afxwin.h> #define IDM_SCROLLBAR 100 const int MIN_RANGE = 0; const int MAX_RANGE = 100; // Declare the application class class CScrollBarApp : public CWinApp { public: virtual BOOL InitInstance(); ; // Create an instance of the application class CScrollBarApp ScrollBarApp; // Declare the main window class class CScrollBarWindow : public CFrameWnd { CScrollBar *sb; public: CScrollBarWindow(); afx_msg void OnHScroll(UINT nsbcode, UINT npos, CScrollBar* pscrollbar); DECLARE_MESSAGE_MAP() ; // The message handler function void CScrollBarWindow::OnHScroll(UINT nsbcode, UINT npos, CScrollBar* pscrollbar) { MessageBeep(-1);
101 // The message map BEGIN_MESSAGE_MAP(CScrollBarWindow, CFrameWnd) ON_WM_HSCROLL() END_MESSAGE_MAP() // The InitInstance function is called once // when the application first executes BOOL CScrollBarApp::InitInstance() { m_pmainwnd = new CScrollBarWindow(); m_pmainwnd->showwindow(m_ncmdshow); m_pmainwnd->updatewindow(); return TRUE; // The constructor for the window class CScrollBarWindow::CScrollBarWindow() { // Create the window itself Create(NULL, _T("CScrollBar Tests"), WS_OVERLAPPEDWINDOW, CRect(0, 0, 300, 200)); // Get the size of the client rectangle CRect r; GetClientRect(&r); // Create a scroll bar sb = new CScrollBar(); sb->create(ws_child WS_VISIBLE SBS_HORZ, CRect(10, 10, r.width()-10, 30), this, IDM_SCROLLBAR); sb->setscrollrange(min_range, MAX_RANGE, TRUE); CScrollBar::Create() 함수는 SBS_HORZ 스타일을사용하여수평스크롤바를만든다 ( 수직스크롤바의경우에는 SBS_VERT 사용 ). 그런다음 CScrollBar::SetScrollRange() 함수에서프로그램의서두에정의되어있는상수 MIN_RANGE와 MAX_RANGE를사용하여스크롤바범위를 0에서 100까지로설정한다. 수평 / 수직스크롤바이벤트를나타내는메시지는 WM_HSCROLL/WM_VSCROLL이며, 이에
102 대한처리기함수는 CWnd::OnHScroll()/CWnd::OnVScroll() 이다. 이함수들은 3개의파라미터를취하는데, 그중첫번째파라미터는사용자의스크롤바에대한동작을나타내는스크롤바코드이다. 스크롤바코드에는아래그림에표시된값들과각기키보드의 HOME, END 키를사용하여얻는 SB_TOP( 또는 SB_LEFT) 과 SB_BOTTOM(SB_RIGHT) 등이있다. 아래그림의스크롤바코드에서 SB_LINEUP, SB_LINEDOWN, SB_PAGEUP, SB_PAGEDOWN은각기 SB_LINELEFT, SB_LINERIGHT, SB_PAGELEFT, SB_PAGERIGHT와같은값을갖는다. 스크롤박스를마우스로끄는동안에는 SB_THUMBTRACK 동작이며, 끄는동작을완료하고마우스버튼에서손을떼는순간은 SB_THUMBPOSITION 동작이된다. 두번째파라미터는스크롤바코드가 SB_THUMBTRACK 또는 SB_THUMBPOSITION일경우스크롤박스의위치를나타낸다. 세번째파라미터는스크롤바컨트롤객체를가리키는포인터인데, 표준스크롤바의경우에는이값은 NULL이다. 아래코드는스크롤바코드에따라스크롤바박스를적절히이동할수있도록 OnHScroll() 함수를고쳐쓴것이다. SB_LINELEFT/RIGHT, SB_PAGELEFT/RIGHT의경우각기 ±1, ±10 만큼이동하게끔프로그램되어있는데, 이값은프로그래머가달리지정할수있다. CScrollBar::SetScrollPos() 함수는파라미터가나타내는위치로스크롤박스를이동시킨다. // The message handling function void CScrollBarWindow::OnHScroll (UINT nsbcode, UINT npos, CScrollBar* pscrollbar) { int pos; pos = sb->getscrollpos(); switch (nsbcode) { case SB_LINELEFT: pos -= 1; break; case SB_LINERIGHT: pos += 1; break;
103 case SB_PAGELEFT: pos -= 10; break; case SB_PAGERIGHT: pos += 10; break; case SB_TOP: pos = MIN_RANGE; break; case SB_BOTTOM: pos = MAX_RANGE; break; case SB_THUMBPOSITION: pos = npos; break; default: return; if (pos < MIN_RANGE) pos = MIN_RANGE; else if (pos > MAX_RANGE) pos = MAX_RANGE; sb->setscrollpos(pos); 헝가리식표기법 (Hungarian Notation) 윈도우 API나 MFC 등에서사용되는함수이름 ( 예 : MessageBeep), 파라미터이름 ( 예 : lpszfacename), 클래스이름 ( 예 : CWinApp), 멤버변수 ( 예 : m_pmainwnd) 등의각종이름에 는헝가리식표기법이라는특별한규칙이적용된다. 이표기법명칭의연원은마이크로소프트 사에서헝가리출신의프로그래머 Charles Simonyi에의해이표기법이창시되었음에따른것 이다. Prefix Variable Type Comment 이름들은표현하고자하는의미 a Array 를나타내는단어들을연결하여만 b Boolean 들어지는데, 각단어의첫글자는 d Double 대문자로표시함으로써단어들을구 h Handle 분한다. 예를들면, Count 또는 i Integer "index into" ClassName 등과같은이름들이사 l Long 용된다. 변수의경우에는이러한의 lp Long pointer to 미전달용이름앞에변수의타입을 lpfn Long pointer to function 나타내는문자들을덧붙인다. 예를 m_ Member variable 들면, ncount 는정수형변수이며, n Integer "number of" bflag는불리언타입변수이다. 특 p Pointer to 히멤버변수의경우에는 m_ 를맨 s String
104 앞에붙인다. 클래스이름앞에는 C sz Zero terminated string 를사용한다. u Unsigned integer 대표적인접두사들을옆의표에 C Class 정리하였지만, 이들외에도많은접두사들이사용되고있다. 지역변수의경우에는특별한규칙이없으며, 오히려멤버변수나파라미터등과구분하기위해 C 언어스타일의소문자와숫자만으로이루어진이름을사용하는것도좋을것이다. 연습문제 1. frame window 와 dialog box 의주요차이점은무엇인가? 2. modal 다이얼로그와 modeless 다이얼로그의차이는무엇인가? 3. MFC library를동적링크라이브러리방식과정적라이브러리방식으로사용하는것의차이는무엇인가? 4. 아래와같은형식의메시지맵에관한질문에답하라. BEGIN_MESSAGE_MAP(class-name-1, class-name-2) ON_... (...); // message handler specifications... END_MESSAGE_MAP() (1) class-name-1 이나타내는클래스의역할은무엇인가? (2) class-name-2 클래스는 class-name-1 클래스가파생되는상위클래스이다. 이상위클래스의역할은무엇인가?
105 4 장대화상자기반응용프로그램 3장에서는윈도우응용프로그램작성을위한 MFC 프로그래밍에텍스트편집기만을사용하였으나, 이장에서부터는응용프로그램마법사 (AppWizard), 리소스편집기, 클래스마법사 (Class Wizard) 등의도구를활용한다. 이러한도구들의이용으로전체프로그램소스에서프로그래머가직접코딩하는분량을크게줄일수있어빠른시간안에응용프로그램의개발이가능해진다. 또한 MFC 프로그램을구성하는부분들사이의연관구조가이러한도구들을통해구성되므로 MFC 프로그램구성에관한노력을현저히줄일수있다. 리소스편집기는컨트롤의배치, 메뉴의구성등등의여러작업을훨씬쉽게해준다. 이러한여러개발도구의이용을가능하게하고많은소스코드의자동생성을통해개발작업의효율을크게높여주는핵심도구가 AppWizard인데, AppWizard는아래와같은세가지전형적인윈도우응용프로그램유형을제공하며, 응용프로그램작성의첫단계에서개발자가유형을선택하면이에맞는소스코드를만들어준다. 대화상자기반응용프로그램 (Dialog based application) 단일문서응용프로그램 (SDI; Single Document Interface application) 다중문서응용프로그램 (MDI; Multiple Document Interface application) 개발자는 AppWizard가만들어준소스코드에필요한리소스와코드를추가하거나수정하여프로그램을완성해나간다. 이장에서는대화상자기반응용프로그램작성예제를몇개들어보이면서 AppWizard를비롯한도구들의사용방법을알아보며, 그다음장들에서 SDI/MDI 응용프로그램작성방법을알아본다. 4.1 AppWizard 를사용한 MFC 프로그래밍 AppWizard 를사용하는응용프로그램의작성은다음과같은세가지절차로이루어진다. AppWizard를사용하는프로젝트생성 리소스편집기를사용하는시각적설계 (visual design) 클래스마법사 (Class Wizard) 를활용하는코딩 위의단계들중프로젝트생성은응용프로그램작성의첫번째단계이며, 한번수행되면다시이단계로돌아오지않는다. 나머지단계들은간단한응용프로그램의경우순차적으로이루어질수도있지만, 많은경우두단계를오가며응용프로그램의작성이이루어진다
106 위의절차들이어떻게이루어지는지알아보기위해다음그림과같은간단한응용프로그램을작성해본다. 이프로그램은주실행창이다이얼로그박스이며, 타이틀바에는 "Dialog Program" 라는프로그램타이틀이나타난다. "Display message" 와 "Exit" 라는캡션을갖는두개의버튼이다이얼로그박스안에들어있다. 이중 "Display message" 버튼을클릭하면오른쪽그림에서와같은메시지박스가나타나며, "Exit" 버튼을누르면주실행창이사라지면서프로그램은종료된다 프로젝트의생성 이전의경우와마찬가지로 [ 파일 ] 메뉴의 [ 새로만들기 ] - [ 프로젝트 ] 라는메뉴항목을선택하되, 아래화면의왼쪽열에서 <Visual C++> 의 <MFC> 라는템플릿을선택하고가운데열에서프로젝트유형으로 <MFC 응용프로그램 > 을선택한다. 프로젝트이름으로는 Dialog라는이름을사용하기로하자. 창하단의 < 확인 > 버튼을누른다
107 아래와같은 <MFC 응용프로그램마법사 > 화면이나타날텐데, 이를이용하여응용프로그램에관한사항들을설정한다. 이화면은 < 다음 > 버튼을눌러서나타나는화면들에따라진행할수도있고, 화면왼쪽의목차를클릭하며필요한사항들을설정할수도있다. 언제든지설정이완료되었다고판단되면 < 마침 > 버튼을누른다. 앞의화면에서 < 다음 > 버튼을누르면아래와같은화면이나타나는데, 여기서는응용프로그램종류와리소스언어를선택한다. 응용프로그램종류로는 " 대화상자기반 " 라디오버튼을선택한다. 리소스언어는컨트롤이나메뉴이름등을위한기본언어를선택하기위한것이다. 예를들어영어를선택하면, 버튼기본캡션으로 "OK", "Cancel" 등이나타나지만, 한국어를선택하면 " 확인 ", " 취소 " 등이나타난다
108 그다음화면에서는대화상자제목을변경할수있는데, 이는프로그램을실행시킬때나타나는창의상단에위치하는타이틀바에표시되는제목을말한다. 이제목은프로젝트이름과같게되어있는데, 이화면에서임의의내용으로변경할수있다. 대화상자제목을 "Dialog Program" 으로변경해보라. 그다음화면은다음과같으며, 변경없이진행한다
109 아래화면에서는 AppWizard가생성하는주요클래스들의이름을보여주고, 이클래스들의부모클래스이름, 이클래스들을선언하는헤더파일의이름, 이클래스들을구현하는소스파일의이름등을보여준다. 응용프로그램에따라서는부모클래스이름을변경함으로써프로그램의기능을변경하는일도있다. 여기서보여주는두개의클래스는응용프로그램클래스 CDialogApp와주실행창을위한 CDialogDlg 클래스이다. CDialogApp 클래스이름중 "Dialog" 는프로젝트이름을나타내며, 이클래스는 CWinApp 클래스를상속받는다. CDialogDlg 클래스는다이얼로그기반응용프로그램의주실행창이기때문에 CFrameWnd 클래스대신 CDialogEx 클래스를상속받는다. 이제 < 마침 > 버튼을누른다. ( 모두저장 ) 버튼을사용하여 AppWizard가만들어준내용들을저장한다음윈도우탐색기로프로젝트폴더를찾아가보면오른쪽그림과같이몇개의폴더들이만들어져있는것을볼수있다. Dialog 폴더안의.sln 파일,.sdf 파일, 그리고 Dialog/Dialog 폴더안의 *.vcproj.* 파일들은 AppWizard를사용하지않던이전의예제들에서도있었던파일들이다
110 그러나 Dialog Dialog 폴더안의나머지파일들과 Dialog Dialog res 폴더안의파일들은 AppWizard가만든파일이다..h 파일들과.cpp 파일들은 C++ 소스파일들인데, 특히 stdafx.h 파일은 MFC 사용을위한헤더파일들을모아둔것이다..rc,.aps 파일들과 res 폴더안의파일들은리소스에관한내용이다. AppWizard가만들어준파일들만으로도컴파일해서실행할수있는완전한프로그램이다. [ 디버깅하지않고시작 ] 메뉴항목을선택하거나단축기 Ctrl+F5를사용하여컴파일및실행해보면오른쪽그림과같은실행화면이나타난다. " 확인 " 버튼이나 " 취소 " 버튼을눌러프로그램을종료시킨다 시각적설계 프로젝트생성마지막단계를마치고 < 마침 > 버튼을누르면다이얼로그기반응용프로그램의경우 Visual Studio는옆의그림과같은모습이된다. ( 이때화면오른쪽의도구상자부분은나타나있지않을수도있고위치를달리하여나타날수도있는데, 도구상자가보이지않을경우화면상단의 [ 보기 ] 메뉴에서 [ 도구상자 ] 메뉴항목을선택하여나타나게할수있다.) 위화면에서클라이언트영역의왼쪽에는프로젝트워크스페이스라고불리는창이있으며, 이창
111 하단에는솔루션탐색기, 클래스뷰, 속성탐색기, 리소스뷰등네개의탭이달려있다. 이탭을사용하여프로그램을구성하고있는파일, 클래스, 속성, 리소스등을일목요연하게볼수있으며, 각요소를창의오른쪽에나타나게만들수도있다. 아래그림에는네개의탭을선택하여안의항목들을펼쳐놓은모습을보이고있다. 프로젝트워크스페이스안의여러항목들을클릭해보고그결과로창의오른쪽에보이는내용들을관찰해보면, 프로젝트워크스페이스의기능을파악하는데도움이될것이다. 언제든지프로젝트워크스페이스의리소스뷰탭을클릭한다음주실행창을나타내는다이얼로그리소스 IDD_DIALOG_DIALOG를선택하면해당다이얼로그를편집할수있는상태가된다. 다른리소스항목의경우도마찬가지인데, 리소스유형마다다이얼로그편집기, 아이콘편집기, 스트링편집기등각기다른편집도구가사용된다. 이러한도구들을모두총칭하는이름이리소스편집기이다. 기본다이얼로그형태는 " 확인 ", " 취소 " 두개의버튼을포함하는것이며, 화면에는 "TODO:..." 라는내용의텍스트컨트롤이추가되어있다. 예제프로그램의경우이들대신 "Display message" 버튼과 "Exit" 버튼을필요로하므로먼저버튼들과텍스트를삭제한다. 삭제는마우스로해당컨트롤을선택한다음 "Del" 키를누르면된다
112 이제다이얼로그의크기를변경시킬수도있고, 컨트롤들을추가할수도있다. 컨트롤을추가할때는왼쪽그림과같은도구상자를사용한다. 컨트롤을추가하는방법은도구상자안의원하는아이콘을클릭한다음다시 IDD_DIALOG_DIALOG 다이얼로그박스안에서클릭하면그위치에해당컨트롤이나타난다. 드래그-앤-드롭방식으로컨트롤을끌어다놓을수도있다. 버튼의크기나위치도마우스를사용하여조정할수있다. 컨트롤에대해 ID, 캡션, 스타일등을포함하는여러속성들을변경할수있다. 컨트롤을클릭하면해당컨트롤에대한속성들이화면오른쪽의속성설정다이얼로그박스에나타난다. 첫번째버튼을위한속성설정다이얼로그박스에서 ID 속성은 IDC_BUTTON1, Caption 속성은 "Button1" 로되어있을것이다. 이를다음그림이나타내는대로각기 IDC_DISPLAY_BUTTON, "Display message" 로수정한다. ( 또는컨트롤을선택한상태에서키보드로캡션텍스트입력을시작하면캡션텍스트가변경된다.) 두번째버튼을위한속성설정다이얼로그박스에서 ID 속성은 IDC_EXIT_BUTT ON, Caption 속성은 "Exit" 로수정한다. 이것으로이프로그램에서시각적설계는완료된다
113 4.1.3 클래스마법사를이용한코딩 이프로그램을위해필요한코딩은두개의버튼에대한동작을위한것이다. 앞장에서이를위해메시지맵과메시지처리기 (Message Handler) 함수정의등의작업을했던것을기억할것이다. 여기에서는클래스마법사를이용하여이러한작업을훨씬간단히할수있음을보게될것이다. 클래스마법사를불러내는방법에는여러가지가있다. Visual Studio의 [ 프로젝트 ] 메뉴에서 [ 클래스마법사 ] 라는메뉴항목을선택할수도있고, Dialog 리소스화면에서버튼을선택한다음팝업메뉴를불러도 [ 클래스마법사 ] 라는메뉴항목이있다. 이러한방법으로클래스마법사를불러내면아래그림과같은 MFC Class Wizard 다이얼로그박스가나타난다. 여러탭중첫번째인 [ 명령 ] 탭이선택되어있어야한다. 프로젝트이름, 클래스이름등을확인한다음, 버튼의개체 ID(=IDC_DISPLAY_BUTTON) 와메시지 (=BN_CLICKED) 등을선택한다. 메시지를선택한후더블클릭하거나오른쪽의 [ 처리기추가 ] 버튼을클릭한다
114 그러면그림에서처럼 [ 멤버함수추가 ] 다이얼로그박스가나타나며, 함수이름은버튼의컨트롤 ID에따라정해지는데, 원한다면함수이름은변경될수있다. 추가된함수는 [ 멤버함수 :] 아래의리스트박스에나타난다. 리스트박스안의함수이름을더블클릭하거나클래스마법사다이얼로그박스오른쪽의 [ 코드편집 :] 버튼을누르면해당함수가구현되어있는소스코드위치로이동하며, 필요한코드를넣을수있다. 컨트롤을위한메시지처리기를추가하기위해클래스마법사를호출하는더욱간단한방법도있는데, 바로리소스편집기에서해당컨트롤을더블클릭하는방법이다. 이렇게하면바로함수편집상태로이동한다. CDialogDlg::OnClickedDisplayButton() 함수에는메시지다이얼로그출력을위해다음함수호출을넣는다. MessageBox(_T("This is a test dialog message.")); CDialogDlg::OnClickedExitButton() 함수에는다음함수호출을넣는다. OnOK() 함수는다이얼로그박스안에서 OK 버튼을누르면불려나오는함수인데, OK 버튼을누르면다이얼로그박스가종료된다. 다이얼로그기반응용프로그램의경우주실행창다이얼로그의종료는응용프로그램종료를의미한다. OnOK();
115 이제코딩작업도완료되었으므로모든파일들을저장한후프로그램을컴파일하여실행할수있을것이다. 연습문제 본문예제에관한다음지시사항들을수행해보라. 1. 프로젝트생성후각파일들의내용을읽어보라. 2. 프로젝트생성후프로젝트워크스페이스의클래스뷰, 리소스뷰, 솔루션탐색기등이보여주는정보가무엇인지확인하라. 3. 메시지처리기함수들을추가한다음클래스뷰를사용하여 CDialogDlg 클래스에서이함수들을찾아보라. 4. 솔루션탐색기에서 DialogDlg.h와 DialogDlg.cpp 파일들을찾아메시지맵과관련하여필요한내용들이어떻게추가되어있는지확인하라. 5. 예제에서사용된 OnOK() 함수는윈도우 API 함수인가? 또는어느클래스의멤버함수인가? 후자의경우 OnOK() 함수가소속되어있는클래스의이름은? OnOK() 함수에대한 Visual Studio 도움말을읽어보라. 6. 예제에서사용된 MessageBox() 함수는윈도우 API 함수인가? 또는어느클래스의멤버함수인가? 후자의경우 MessageBox() 함수가소속되어있는클래스의이름은? MessageBox() 함수에대한 Visual Studio 도움말을읽어보라. 어떤파라미터들을취하는가? 파라미터들을변경해보고그효과를관찰하라. 7. 버튼에대한속성설정다이얼로그박스에서 Client edge, Static edge, Modal Frame 등의스타일속성을찾아적용해보라. 8. 다이얼로그자체에대한속성설정다이얼로그박스에서어떠한스타일과속성들이있는지살펴보라. 특히응용프로그램타이틀과다이얼로그안의폰트를변경시켜보라
116 4.2 메뉴및컨트롤들의추가 이절에서다룰예제프로그램은덧셈 / 곱셈문제를제시하고답을검사해주는프로그램이다. 이프로그램을실행하면두개의한자리숫자들이무작위로생성되어 "Number 1" 과 "Number 2" 옆의에디트컨트롤에나타난다. Exec 메뉴안에는 Renew라는메뉴항목이있으며, 이메뉴항목을선택할때마다숫자들은무작위로변경된다. Exec 메뉴안의 Change Level 메뉴항목을선택한후 Renew를사용하면두자리난수가발생되며, Change Level 메뉴항목을다시선택하면다시한자리숫자로돌아간다. Name 옆의에디트컨트롤에는사용자이름을입력하며, 라디오버튼을사용하여문제유형이덧셈인지곱셈인지를선택한다. Answer 옆의에디트컨트롤에는답을입력한다. Exec 메뉴안의 Check 메뉴항목을선택하면, 답이맞는지확인하여알맞은메시지다이얼로그박스를띄워준다. 이예제를통해살펴볼내용은다음과같다. 텍스트컨트롤, 에디트컨트롤, 라디오버튼컨트롤등의컨트롤이용방법 특히에디트컨트롤이나라디오버튼컨트롤의경우에는사용자의입력값또는선택을나타내는변수가있으며, 다이얼로그박스안의컨트롤들에대해변수를연계시켜처리하는방법을다룬다. 다이얼로그박스에메뉴사용 메뉴리소스는 SDI/MDI 응용프로그램유형의경우에는 AppWizard에의해자동적으로포함된다. 기본형다이얼로그기반응용프로그램에서는메뉴를사용하지않는데, 이절에서는메뉴를다이얼로그박스에추가하는방법을설명한다. 또이를통해메뉴의편집, 리소스의추가등의주제를다룬다
117 4.2.1 프로젝트의생성 이작업은다음사항을제외하고는 4.1 절의경우와같다. 프로젝트이름을 Arith로정한다. 리소스언어를영어 [ 미국 ] 으로정한다. 이경우 Visual Studio 6 등의예전버전의 Visual Studio에서는타이틀바나컨트롤캡션에한글을사용하면실행시한글대신깨진글자들이나타났었지만최근버전들에서는이러한문제는해결된것으로보인다 시각적설계 이응용프로그램의경우시각적설계는다이얼로그설계와메뉴설계로나누어진다. 다이얼로그에는옆의그림과같이 4개의에디트컨트롤과이들을설명하는 4개의텍스트컨트롤, 그리고 2개의라디오버튼그룹등이포함된다. 다이얼로그박스자체를포함하여컨트롤들의속성은오른쪽표와같이정한다. 다이얼로그에서는타이틀을 "Arithmetic Practice Program" 으로정하고, 최소화및최대화버튼을추가하고, 폰트를바탕 10pt로변경한다. 에디트컨트롤들중 "Number 1", "Number 2" 옆의것들은비활성화 (disabled) 되어있어사용자가내용을변경할수없고, 프로그램안에서계산된값을표시하는용도로사용된다. 라디오버튼들은여러개가하나의그룹을형성하도록되어있으며, 한그룹안의라디오버튼들중한개만선택할수있도록되어있다. 이중한버튼을클릭하여선택하 Object Property Setting Dialog Box ID Caption Font (Size) Minimize box Maximize box IDD_ARITH_DIALOG Arithmetic Practice Program 바탕 (10) True True Static Text Caption Name Static Text Caption Number 1 Static Text Caption Number 2 Static Text Caption Answer Edit Box ID IDC_NAME_EDIT Edit Box Edit Box ID Disabled ID Disabled IDC_NUM1_EDIT True IDC_NUM2_EDIT True Edit Box ID IDC_ANSWER_EDIT Radio Button Radio Button ID Caption Group ID Caption Group IDC_ADD_RADIO Addition True IDC_MULT_RADIO Multiplication False
118 면기존에선택되어있던라디오버튼의선택은해제된다. 라디오버튼그룹은 group 속성이 True로설정되어있는라디오버튼과탭순서번호상그후에나타나는라디오버튼들로이루어진다. group 속성이 True로설정되어있는라디오버튼이다시나타나면새로운그룹이시작된다. 메뉴를만들기위해먼저 Visual Studio의 [ 프로젝트 ] 메뉴의 [ 리소스추가 ] 메뉴항목을선택하면리소스유형선택을위한다이얼로그박스가오른쪽그림과같이나타난다. [Menu] 를선택한다음 [ 새로만들기 ] 버튼을클릭하면메뉴편집화면이나타난다. 메뉴편집화면에서 Exec와 Exit라는이름의메뉴들을만든다음그림에나타난대로메뉴항목들을만든다. 메뉴, 메뉴항목, 컨트롤등의캡션을정할때밑줄표시된문자가포함되어있는경우를흔히볼수있다. 이러한문자는마우스대신키보드를사용하여메뉴나컨트롤에접근할수있게하기위한것으로편의키 (mnemonic key) 라고불린다. 편의키의설정을위해해당문자앞에 & 기호를덧붙인다. 예를들면, E&xit는 Exit라고표시된다. Exec 메뉴안의처음두메뉴항목과마지막메뉴항목사이에는분리선이표시되어있는데, 이를위해서는메뉴항목을입력하는대신 [ 구분선삽입 ] 메뉴항목을사용한다..rc 파일은리소스들에대한정의를갖고있으며, 리소스컴파일러라는도구에의해.res 파일로변환되어프로그램에서이용된다. 아래내용은메뉴설계가완료된후.rc 파일의내용중메뉴관련부분만추출한것이다. 이내용대로각메뉴항목을위한객체 ID를정해준다
119 ///////////////////////////////////////////////////////////////////////////// // // Menu // IDR_MENU1 MENU BEGIN POPUP "&Exec" BEGIN MENUITEM "&Renew", MENUITEM "&Check", MENUITEM SEPARATOR MENUITEM "Change &Level", END POPUP "E&xit" BEGIN MENUITEM "&Quit", MENUITEM "&Save and Quit", END END ID_EXEC_RENEW ID_EXEC_CHECK ID_EXEC_CHANGELEVEL ID_EXIT_QUIT ID_EXIT_SAVEANDQUIT 메뉴설계가완료된후 IDD_ARITH_DIALOG 다이얼로그에게 IDR_MENU1이라는메뉴를사용하도록알려주어야한다. 이를위해 IDD_ARITH_DIALOG 다이얼로그의 Menu 속성을메뉴의리소스 ID인 IDR_MENU1으로설정한다 클래스마법사를사용하는코딩 다이얼로그상의버튼컨트롤이나메뉴항목등의경우클릭을통해당장어떤작업이수행될것을기대한다. 따라서이와같은이벤트로인해발생하는메시지를처리하는함수를메시지맵을통해규정하는것으로관련코딩이완료된다. 반면에디트컨트롤이나라디오버튼의경우사용자입력이나선택을변수를통해저장해두었다가나중의처리과정에서저장된값들을이용하게된다. 이를위해이와같은컨트롤에는연계변수를설정한다. 연계변수의설정을위해멤버변수추가마법사를사용할수도있지만, 여기에서는클래스
120 마법사를이용한다. ( 멤버변수추가마법사를호출하는방법들에대해서는스스로찾아보기바란다.) 변수를연계시킬컨트롤을선택한후팝업메뉴를통해클래스마법사를호출하면컨트롤 ID 리스트박스에해당컨트롤의 ID가선택되어있을것이다. 물론 [ 프로젝트 ] 메뉴에서 [ 클래스마법사 ] 메뉴항목을선택한후컨트롤 ID를나중에선택해도무방하다. 클래스마법사다이얼로그에서 [ 멤버변수 ] 탭이선택되어있음을확인해야하며, 또클래스이름이컨트롤들을담고있는다이얼로그클래스임을확인한다. 이는컨트롤연계변수들이다이얼로그클래스의멤버변수가될것임을의미한다. 변수연계가필요한컨트롤들의 ID에는 4개의에디트컨트롤을위한 IDC_NAME_EDIT, IDC_NUM1_EDIT, IDC_NUM2_EDIT, IDC_ANSWER_EDIT 등과그룹속성을갖는라디오버튼을위한 IDC_ADD_RADIO 등이있다. 해당 ID를더블클릭하거나해당 ID를선택한후 [ 변수추가 ] 버튼을클릭하면 [ 멤버변수추가 ] 다이얼로그박스가나타난다. 에디트컨트롤에연계될변수의타입으로는스트링 (CString) 또는정수 (int, UINT, long,...) 가허용되며, 라디오버튼그룹에연계될변수의타입으로는정수 (int) 또는 Bool만허용된다. 연계변수의 [ 범주 ] 속성으로는 Control 대신반드시 Value가선택되어야한다
121 라디오버튼그룹에는 m_nradio (int 타입 ), IDC_NAME_EDIT, IDC_NUM1_EDIT, IDC_NUM2_EDIT, IDC_ANSWER_EDIT 등에대해서는각기 m_strname (CString 타입 ), m_nnum1 (int 타입 ), m_nnum2 (int 타입 ), m_nanswer (int 타입 ) 등으로정한다. 이렇게다이얼로그상에나타나는컨트롤들에연계된변수들을 DDX(Dialog Data Exchange) 변수라고부르는데, 값들은다이얼로그화면에서변수로이동하거나또는그역방향으로이동한다. 이러한이동은다이얼로그클래스안의멤버함수 DoDataExchange() 안에서이루어지는데, 프로그래머는 UpdateData() 함수를호출하여값의교환이일어나게할수있다. UpdateData() 함수는 DoDataExchange() 함수를호출한다. UpdateData(TRUE) 는다이얼로그화면상의값을변수에저장시키는데, 이때인수 TRUE는디폴트인수이므로생략될수있다. UpdateData(FALSE) 는변수들이갖고있는값을화면에나타나게만든다. 다이얼로그가화면에처음나타날때 UpdateData(FALSE) 가자동적으로실행된다. 그리고다이얼로그안의 [ 확인 ] 버튼을클릭하여다이얼로그를닫을때 UpdateData(TRUE) 가자동적으로실행된다. DDX 변수에는정수의경우최소값이나최대값을설정할수있으며 ( 아래그림하단부분을참고 ), 스트링의경우최대길이를설정할수있다. 이러한제한값들을 DDV(Dialog Data Validation) 값이라고하며, 사용자가이러한제한을어기면자동적으로경고메시지가뜨게된다. (m_nanswer의범위는 1~9999, m_strname의최대길이는 10으로설정하라. 그리고이러한제약을벗어난값들을입력한후발생하는현상을관찰해보라.) Change Level 메뉴항목은클릭할때마다무작위생성할숫자크기로한자릿수와두자릿수사이를오가게하며, 어떤자릿수가현재선택되어있는지를나타내는변수가필요하다. 이프로그램에서한자릿수는 1에서 9 사이의숫자, 두자릿수는 1에서 99 사이의숫자를의미하는것으로한다. 자릿수를나타내는변수는 m_nlimit(int 타입 ) 으로정하고, 그값은 9와 99 사이를오가도록한다. ( 이변수는 1 + rand() % m_nlimit 라는식에서사용된다.) 컨트롤과무관한클래스의멤버변수를포함시키는방법은여러가지가있다. 프로젝트워크스페이스에서솔루션탐색기로가서해당클래스를선언하고있는헤더파일을열어클래스선언안에멤버변수를선언하는것도한방법이다. 여기서는아래그림과같이클래스뷰에서클래스이름을선택한다음팝업메뉴를연다음 [ 추가 - 변수추가 ] 메뉴항목을선택하여나타나는멤버변수추가마법사를이용한다. 이로써멤버변수들에대한선언은완료되었으며, 남은부분은필요한메시지처리기함수들을정의해주는일이다. 이프로그램에서처리기함수를필요로하는것은 5개의메뉴항목이다. 그리고프로그램실행시필요한초기화작업은다이얼로그클래스의생성자인 CArithDlg() 함수나멤버함수
122 OnInitDialog() 에서해줄수있는데, 여기서는 OnInitDialog() 함수안에서초기화를처리하기로 한다. 아래에멤버함수관련사항을객체 ID, 메시지 ID, 함수이름등의순서로요약하고있 다. CArithDlg - WM_INITDIALOG - OnInitDialog() : 편집 IDC_EXEC_RENEW - COMMAND - OnExecRenew() : 새로정의 IDC_EXEC_CHECK - COMMAND - OnExecCheck() : 새로정의 IDC_EXEC_CHANGELEVEL - COMMAND - OnExecChangelevel() : 새로정의 IDC_EXIT_QUIT - COMMAND - OnExitQuit() : 새로정의 IDC_EXIT_SAVEANDQUIT - COMMAND - OnExitSaveandquit() : 새로정의 다이얼로그클래스헤더파일및구현파일 아래소스에서음영처리된부분은관심을갖고살펴볼부분을나타낸다. // 편집 - 추가 라고표시되어있는부분은멤버함수중추가코딩작업한부분을나타낸다. // ArithDlg.h : header file // #pragma once // CArithDlg dialog class CArithDlg : public CDialogEx { // Construction public: CArithDlg(CWnd* pparent = NULL); // standard constructor // Dialog Data enum { IDD = IDD_ARITH_DIALOG ; protected: virtual void DoDataExchange(CDataExchange* pdx); // DDX/DDV support // Implementation protected: HICON m_hicon; public: // Generated message map functions virtual BOOL OnInitDialog(); afx_msg void OnSysCommand(UINT nid, LPARAM lparam); afx_msg void OnPaint(); afx_msg HCURSOR OnQueryDragIcon(); DECLARE_MESSAGE_MAP() int m_nradio; int m_nanswer; CString m_strname;
123 ; int m_nnum1; int m_nnum2; int m_nlimit; afx_msg void OnExecRenew(); afx_msg void OnExecChangelevel(); afx_msg void OnExecCheck(); afx_msg void OnExitQuit(); afx_msg void OnExitSaveandquit(); // ArithDlg.cpp : implementation file // #include "stdafx.h" #include "Arith.h" #include "ArithDlg.h" #include "afxdialogex.h" #ifdef _DEBUG #define new DEBUG_NEW #endif // CAboutDlg dialog used for App About class CAboutDlg : public CDialogEx { public: CAboutDlg(); // Dialog Data enum { IDD = IDD_ABOUTBOX ; protected: virtual void DoDataExchange(CDataExchange* pdx); // DDX/DDV support // Implementation protected: DECLARE_MESSAGE_MAP() ; CAboutDlg::CAboutDlg() : CDialogEx(CAboutDlg::IDD) { void CAboutDlg::DoDataExchange(CDataExchange* pdx)
124 { CDialogEx::DoDataExchange(pDX); BEGIN_MESSAGE_MAP(CAboutDlg, CDialogEx) END_MESSAGE_MAP() // CArithDlg dialog CArithDlg::CArithDlg(CWnd* pparent /*=NULL*/) : CDialogEx(CArithDlg::IDD, pparent), m_nlimit(0) { m_hicon = AfxGetApp()->LoadIcon(IDR_MAINFRAME); m_nradio = 0; m_nanswer = 0; m_strname = _T(""); m_nnum1 = 0; m_nnum2 = 0; void CArithDlg::DoDataExchange(CDataExchange* pdx) { CDialogEx::DoDataExchange(pDX); DDX_Radio(pDX, IDC_ADD_RADIO, m_nradio); DDX_Text(pDX, IDC_ANSWER_EDIT, m_nanswer); DDV_MinMaxInt(pDX, m_nanswer, 1, 9999); DDX_Text(pDX, IDC_NAME_EDIT, m_strname); DDV_MaxChars(pDX, m_strname, 10); DDX_Text(pDX, IDC_NUM1_EDIT, m_nnum1); DDX_Text(pDX, IDC_NUM2_EDIT, m_nnum2); BEGIN_MESSAGE_MAP(CArithDlg, CDialogEx) ON_WM_SYSCOMMAND() ON_WM_PAINT() ON_WM_QUERYDRAGICON() ON_COMMAND(ID_EXEC_RENEW, &CArithDlg::OnExecRenew) ON_COMMAND(ID_EXEC_CHANGELEVEL, &CArithDlg::OnExecChangelevel) ON_COMMAND(ID_EXEC_CHECK, &CArithDlg::OnExecCheck) ON_COMMAND(ID_EXIT_QUIT, &CArithDlg::OnExitQuit) ON_COMMAND(ID_EXIT_SAVEANDQUIT, &CArithDlg::OnExitSaveandquit) END_MESSAGE_MAP()
125 // CArithDlg message handlers BOOL CArithDlg::OnInitDialog() { CDialogEx::OnInitDialog(); // Add "About..." menu item to system menu. // IDM_ABOUTBOX must be in the system command range. ASSERT((IDM_ABOUTBOX & 0xFFF0) == IDM_ABOUTBOX); ASSERT(IDM_ABOUTBOX < 0xF000); CMenu* psysmenu = GetSystemMenu(FALSE); if (psysmenu!= NULL) { BOOL bnamevalid; CString straboutmenu; bnamevalid = straboutmenu.loadstring(ids_aboutbox); ASSERT(bNameValid); if (!straboutmenu.isempty()) { psysmenu->appendmenu(mf_separator); psysmenu->appendmenu(mf_string, IDM_ABOUTBOX, straboutmenu); // Set the icon for this dialog. The framework does this automatically // when the application's main window is not a dialog SetIcon(m_hIcon, TRUE); // Set big icon SetIcon(m_hIcon, FALSE); // Set small icon // TODO: Add extra initialization here m_nlimit = 9; m_nradio = 0; srand ((int) time (NULL)); m_nnum1 = 1 + rand()%m_nlimit; m_nnum2 = 1 + rand()%m_nlimit; UpdateData (FALSE); // 편집 - 추가 // 편집 - 추가 // 편집 - 추가 // 편집 - 추가 // 편집 - 추가 // 편집 - 추가 return TRUE; // return TRUE unless you set the focus to a control void CArithDlg::OnSysCommand(UINT nid, LPARAM lparam)
126 { if ((nid & 0xFFF0) == IDM_ABOUTBOX) { CAboutDlg dlgabout; dlgabout.domodal(); else { CDialogEx::OnSysCommand(nID, lparam); // If you add a minimize button to your dialog, you will need the code below // to draw the icon. For MFC applications using the document/view model, // this is automatically done for you by the framework. void CArithDlg::OnPaint() { if (IsIconic()) { CPaintDC dc(this); // device context for painting SendMessage(WM_ICONERASEBKGND, reinterpret_cast<wparam>(dc.getsafehdc()), 0); // Center icon in client rectangle int cxicon = GetSystemMetrics(SM_CXICON); int cyicon = GetSystemMetrics(SM_CYICON); CRect rect; GetClientRect(&rect); int x = (rect.width() - cxicon + 1) / 2; int y = (rect.height() - cyicon + 1) / 2; else { // Draw the icon dc.drawicon(x, y, m_hicon); CDialogEx::OnPaint(); // The system calls this function to obtain the cursor to display while the user drags // the minimized window
127 HCURSOR CArithDlg::OnQueryDragIcon() { return static_cast<hcursor>(m_hicon); void CArithDlg::OnExecRenew() { // TODO: 여기에명령처리기코드를추가합니다. m_nnum1 = 1 + rand()%m_nlimit; m_nnum2 = 1 + rand()%m_nlimit; UpdateData (FALSE); // 편집 - 추가 // 편집 - 추가 // 편집 - 추가 void CArithDlg::OnExecChangelevel() { // TODO: 여기에명령처리기코드를추가합니다. m_nlimit = (9 + 99) - m_nlimit; // 편집 - 추가 void CArithDlg::OnExecCheck() { // TODO: 여기에명령처리기코드를추가합니다. int correct_answer; // 편집 - 추가 // 편집 - 추가 UpdateData(); // 편집 - 추가 if (m_nradio == 0) // 편집 - 추가 correct_answer = m_nnum1 + m_nnum2; // 편집 - 추가 else // 편집 - 추가 correct_answer = m_nnum1 * m_nnum2; // 편집 - 추가 // 편집 - 추가 if (m_nanswer == correct_answer) // 편집 - 추가 MessageBox (_T("Correct. Congratulations, ") + m_strname); // 편집 - 추가 else // 편집 - 추가 MessageBox (_T("Wrong. Try again, ") + m_strname); // 편집 - 추가 void CArithDlg::OnExitQuit() { // TODO: 여기에명령처리기코드를추가합니다. OnOK(); // 편집 - 추가 void CArithDlg::OnExitSaveandquit()
128 { // TODO: 여기에명령처리기코드를추가합니다. OnOK(); // 편집 - 추가 연습문제 본문예제에관한다음지시사항들을수행해보라. 9. 프로그램타이틀캡션이나컨트롤캡션에한글텍스트를입력한후실행시의결과를확인해보라. 10. CWnd:UpdateData() 함수의기능을설명하라? 11. Answer 옆의에디트컨트롤연계변수에 1에서 9999 사이의값으로제한하는 DDV 기능을사용한다음, 실행시범위를벗어나는값을입력해보라. 어느시점에서 DDV 확인이이루어지는지관찰한다음그이유를설명해보라. 12. 라디오버튼그룹에나눗셈을위한라디오버튼을추가하라. 이경우몫과나머지를입력하기위한에디트컨트롤이나타나도록해야한다. ( 참고 : 라디오버튼선택이벤트를나타내는메시지는 ON_BN_CLICKED이다.) 13. 정답출력을위한메뉴항목을추가해보라. 정답을어떤방식으로제시할것인지는스스로결정한다. 14. 라디오버튼그룹을리스트박스컨트롤이나콤보박스컨트롤로대체해보라. 15. 현재는문제를두개의숫자와라디오버튼을이용해나타낸다. 라디오버튼들을없애고문제유형개수만큼의 Renew 메뉴항목을만든다음문제는하나의에디트컨트롤에다음과같은식의형태로나타나게만들어보라 = 23 * 32 = 34 % 5 =
129 4.3 그래픽프로그래밍 이절에서다룰예제응용프로그램은사각형이나타원을그리거나마우스이동위치를추적하여연결함으로써자유곡선을그리는등의그래픽작업을다루는프로그램이다. 어떤도형을그릴것인지는라디오버튼으로정한다. 타원을그릴때는매번타원모양, 테두리선의굵기와색상, 채우기패턴등을변경하는데, 이렇게변경된그래픽속성이다른도형을그릴때에도적용된다. 즉, 타원을그릴때는그래픽속성이매번변경되지만, 다른도형을그릴때는마지막타원을그렸을때의속성이계속적용된다. 사각형이나타원의위치는마우스클릭에의해정해진다 그래픽관련 MFC 클래스 그래픽프로그래밍을하기위해서는두부류의 MFC 클래스들을이용하게되는데, 바로디바이스콘텍스트관련클래스들과그리기도구관련클래스들이다. 디바이스콘텍스트및관련클래스 디바이스콘텍스트 (device context) 란모니터화면, 프린터, 플로터, 통신용모뎀등의그래픽출력장치에관한정보, 그리기속성관련정보등을갖고있는윈도우자료구조체 ( 객체 ) 를말한다. MFC가제공하는디바이스콘텍스트를사용함으로써윈도우응용프로그램에서는실제출력장치에상관없이동일한코드를사용할수있다. 그리기속성이란선의모양, 굵기,
130 색상, 채우기색상이나패턴, 폰트등등의다양한요소가포함된다. 그리기속성을포함하는 DC의옵션들은기본값들이정해져있으며, 이들은변경될수있다. 디바이스콘텍스트를그리기기능함수 (CDC 클래스의멤버함수 ) 나타내는기본클래스는선 MoveTo, LineTo CDC이다. CDC 클래스는사각형 Rectangle, FillRect, FrameRect, Draw3dRect 또한선분그리기, 사각형타원 Ellipse 파이 Pie 그리기, 타원그리기등의호 Arc 다양한도형을그릴수있다각형 Polygon 는멤버함수들을갖고있베지어곡선 PolyDraw, PolyBezier, PolyBezierTo 영역의경계선 FrameRgn 으며, 이러한함수들은디바이스콘텍스트에저장된그리기속성정보를참고한다. 그래픽작업은디바이스콘텍스트객체를생성한후, 이에대해그림그리기관련함수를호출함으로써이루어진다. CDC는 200개정도의멤버함수를갖는방대한클래스인데, 그래픽출력영역에따라특화된 CClientDC, CWindowDC, CPaintDC, CMetaFileDC 등 4개의자식클래스들이정의되어있다. 이들중 CClientDC, CWindowDC, CPaintDC 등은그리기대상영역이창과연관되어있으며, 따라서모니터화면의경우에만적용될수있다. CMetaFileDC는그림을지금그리는대신그래픽명령을저장해두었다가나중에재생하기위한메타파일을나타낸다. 창이외의영역을대상으로하거나프린터등비-화면출력장치를사용할경우에는 CDC 객체가사용된다. 물론 CDC 객체는창을출력대상으로하는경우에도적용될수있다. CClientDC 클래스 : 지정된윈도우의클라이언트영역에출력한다. this가현재윈도우를가리키는포인터라고하면, 아래코드는현재윈도우의클라이언트영역안에사각형을그리게된다. CClientDC dc(this); dc.rectangle(10,20,80,90); // (10,20) 은좌측상단 (80,90) 은우측하단 CWindowDC 클래스 : 타이틀바, 메뉴바, 스크롤바등과같이클라이언트영역이아닌곳도출력영역에포함시키고자할때사용한다. CPaintDC 클래스 : 창의일부또는전체가가려졌다가다시나타나는등의이유로윈도우의클라이언트의내용이다시그려질필요가있을때, WM_PAINT라는메시지가전달된다. 이메시지를처리하는함수가 CWnd::OnPaint() 인데, 이함수에전달되는디바이스콘텍스트가바로 CPaintDC 타입의객체를가리키는포인터이다. CPaintDC 타입의객체가가리키는부분은클라이언트영역중가려졌다가다시나타나는부분으로국한된다. CMetaFileDC 클래스 : 원하는그림의생성을위해재생될수있는그래픽출력명령 sequence를담고있는메타파일을나타낸다
131 그리기도구관련클래스 DC 옵션들은여러개의카테고리로그룹화되어있으며, 이러한 DC 옵션카테고리를나타내는클래스들이있다. 이러한클래스들의인스턴스를 GDI(Graphics Device Interface) 객체들이라고하며, 이들을나타내기위한클래스에는다음과같은것들이있다. CPen CBrush CFont CPalette CBitmap CRgn 선이나영역경계선의색상, 두께, 패턴폐곡선영역내부를채울때사용되는픽셀의색, 패턴문자출력시글꼴모양, 크기등색상수가제한되는상황에서실제로출력될색상세트비트맵그림그림그릴영역을나타내는타원또는다각형설정 이러한클래스들은 DC 상에그림을그리는도구들을표현하고있으며, DC 옵션설정에사용된다. DC 옵션들을설정하기위해서는해당옵션을위한 GDI 객체를생성하고관련옵션값들을설정한후특정 DC에연결하는절차가필요하다. 아래의코드는 CPen 객체를이용하여선그리기옵션변경을해보이고있다. CPen pen; pen.createpen(ps_solid, 3, RGB(255, 0, 0)); CClientDC dc(this); CPen *poldpen = (CPen *) dc.selectobject(&pen); dc.rectangle(10, 10, 100, 100); dc.selectobject(poldpen); CPen::CreatePen() 함수의첫번째파라미터는 PS_SOLID 선모양을나타내는데, 이를위해오른쪽그림과 PS_DASH 같은상수들이정의되어있다. 두번째파라미터 PS_DOT 는선의굵기를나타내는데, 선의굵기가 2 이상 PS_DASHDOT 이면첫번째파라미터값에상관없이선모양은 PS_DASHDOTDOT PS_SOLID가된다. 세번째파라미터는선의색상을나타내는데, RGB(r, g, b) 마크로를사용하 PS_NULL 여각기 사이의 red, green, blue 색상 PS_INSIDEFRAME 값을조합하여표현한다. CDC::SelectObject() 함수는 GDI 객체에대한포인터를파라미터로취하며, GDI 객체가설정하는그리기옵션을 DC에반영한다. 이함수는변경전옵션을나타내는 GDI 객체에대한포인터를리턴하는데, 이를이용하여원래옵션으로복원시킬수있다
132 그리기함수사용예 CPaintDC dc(this); CBrush bluebrush; bluebrush.createsolidbrush(rgb(0, 0, 255)); CBrush redbrush; redbrush.createsolidbrush(rgb(255, 0, 0)); dc.moveto(0, 10); dc.lineto(30, 30); dc.rectangle(40, 10, 80, 30); dc.fillrect(crect(90, 10, 130, 30), &bluebrush); dc.framerect(crect(140, 10, 180, 30), &bluebrush); dc.draw3drect(190, 10, 40, 20, RGB(255, 0, 0), RGB(0, 255, 0)); dc.ellipse(240, 10, 280, 30); dc.pie(290, 10, 330, 30, 300, 20, 320, 25); dc.arc(290, 10, 330, 30, 320, 15, 300, 15); CPoint pts[7] = { CPoint(20, 80), CPoint(60, 160), CPoint(140, 200), CPoint(160, 160), CPoint(160, 120), CPoint(140, 100), CPoint(60, 80); dc.polygon(pts, 7); CPen redpen; redpen.createpen(ps_solid, 3, RGB(255, 0, 0)); dc.selectobject(&redpen); dc.polybezier(pts, 7); CRgn rgn1, rgn2; CPoint pts2[6] = { CPoint(320, 60), CPoint(320, 100), CPoint(350, 120), CPoint(380, 100), CPoint(380, 60), CPoint(350, 50); rgn1.createellipticrgn(200, 40, 300, 100); rgn2.createpolygonrgn(pts2, 6, ALTERNATE); dc.framergn(&rgn1, &bluebrush, 10, 2); dc.framergn(&rgn2, &bluebrush, 10, 2); dc.selectcliprgn(&rgn1, RGN_AND); dc.fillrect(crect(100, 50, 400, 80), &redbrush);
133 글꼴과텍스트출력 MFC에서는텍스트출력도그래픽작업으로취급된다. 즉, 관련 GDI 객체로 CFont를사용하여 CDC 객체에대해텍스트출력함수 TextOut() 이나 DrawText() 등을사용한다. LOGFONT 는논리적글꼴의표현을위한구조체이며, 다음과같이정의되어있다. typedef struct taglogfontw { LONG lfheight; // 높이 LONG lfwidth; // 너비 LONG lfescapement; // 방향 LONG lforientation; // 회전각도 LONG lfweight; // 굵기 BYTE lfitalic; // 기울임꼴 BYTE lfunderline; // 밑줄 BYTE lfstrikeout; // 취소선 BYTE lfcharset; // 문자세트 BYTE lfoutprecision; // 출력정확도 BYTE lfclipprecision; // 클리핑정확도 BYTE lfquality; // 출력의질 BYTE lfpitchandfamily; // 자간 TCHAR lffacename[lf_facesize]; // 글꼴이름 LOGFONT; 글꼴의세부사항을설정하기위해사용되는 CFont::CreateFont() 함수의파라미터들은 LOGFONT 구조체의각필드에대응된다. BOOL CreateFont(int nheight, int nwidth, int nescapement, int norientation, int nweight, BYTE bitalic, BYTE bunderline, BYTE cstrikeout, BYTE ncharset, BYTE noutprecision, BYTE nclipprecision, BYTE nquality, BYTE npitchandfamily, LPCTSTR lpszfacename); 폰트의생성과설정을위한코드예는다음과같다. CFont newfont, *poldfont; newfont.createfont(100, 0, 0, 0, 400, FW_NORMAL, FALSE, FALSE, DEFAULT_CHARSET, OUT_CHARACTER_PRECIS, CLIP_CHARACTER_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH FF_DONTCARE, _T( Times New Romans )); poldfont = (CFont *)pdc->selectobject(&newfont);
134 텍스트출력함수들의프로토타입은다음과같다. BOOL TextOut(int x, int y, const CString& str); int DrawText(const CString& str, LPRECT lprect, UINT nformat); 간단한텍스트출력코드예는다음과같다. dc.textout() 함수는 DC 상에서좌표 (40, 60) 에서시작하여스트링 "Hello" 를출력한다. dc.drawtext() 함수는현재윈도우의클라이언트영역의정중앙에한줄로스트링 "Hello" 를출력한다. dc.textout(40, 60, Hello ); CRect rect; GetClientRect(&rect); // 클라이언트영역의크기를얻어냄 dc.drawtext("hello", &rect, DT_SINGLELINE DT_CENTER DT_VCENTER); 텍스트색상및배경색상변경은다음과같은코드에의해이루어질수있다. dc.settextcolor(rgb(255, 0, 0)); dc.setbkcolor(rgb(0, 0, 255)); 텍스트배경모드변경은다음과같은코드에의해이루어질수있다. dc.setbkmode(transparent); dc.setbkmode(opaque); // 투명 // 불투명 : 배경색상적용 예제프로그램의작성 이절의예제프로그램을위한프로젝트생성단계는이전의예제들과같다. 시각적설계단계도다이얼로그박스안에하나의그룹을형성하는 3개의라디오버튼을만들고이들의캡션은각기 "Square", "Ellipse", "Free Drawing" 등으로정한다. 다이얼로그클래스멤버변수는다음과같다. m_nshape: 라디오버튼그룹에연계된변수 m_pointold: Free Drawing의경우마우스의이전위치를나타내는 CPoint 타입의 private 변수 m_bmousedown: 마우스왼쪽버튼이상태를나타내는 bool 타입의 private 변수
135 이프로그램에서처리기함수를요하는이벤트는마우스왼쪽버튼누르기, 마우스왼쪽버튼풀어주기, 마우스이동등이며, 이들은각기다이얼로그창에대한 WM_LBUTTONDOWN, WM_LBUTTONUP, WM_MOUSEMOVE 등의메시지에해당된다. 클래스마법사를부르고 [ 메시지 ] 탭을선택한다음상단의 < 클래스이름 > 아래의이름이다이얼로그클래스 CGraphicsDlg임을확인한다. 클래스마법사화면이나타내는것처럼 < 메시지 > 아래의리스트박스에서 WM_LBUTTONDOWN, WM_LBUTTONUP, WM_MOUSEMOVE 등의메시지들에대해처리기함수를추가한다. CGraphicsDlg::OnInitDialog() 안에다음문장을추가하여처음에는마우스왼쪽버튼이눌려져있지않음을나타낸다. m_bmousedown = FALSE;
136 CGraphicsDlg 클래스의 OnLButtonDown(), OnLButtonUp(), OnMouseMove() 함수들은아래와같이작성한다. 이들에앞서전역변수 pdc를다음과같이선언하는데, 이변수는 OnLButtonDown() 함수에서설정한 DC를 OnMouseMove() 함수에서도공유할수있도록하기위해선언된다. OnLButtonDown() 함수는두개의파라미터를취한다. 첫번째파라미터는마우스버튼이나키보드 (ctrl, shift) 상태를나타내며이예제에서는사용되지않고있다. 두번째파라미터는마우스의현재위치를나타내는좌표이다. 이함수에서는먼저현재다이얼로그창의클라이언트영역을출력대상으로하는 DC 객체 dc를 static으로생성하여이객체의생성을불필요하게반복하지않도록한다. 이객체에대한포인터를전역변수 pdc에저장해둔다. UpdateData() 함수는라디오버튼선택을 m_nshape 변수에반영한다. 그런다음 m_nshape의값에따라필요한처리를한다. m_nshape가 0일경우 CDC::Rectangle() 함수를사용하여마우스가가리키는위치를좌측상단으로하고한변의길이가 20인정사각형을그린다. m_nshape가 1일경우에는 CDC::Ellipse() 함수를사용하여타원을그린다. 타원의크기는난수를사용하여매번변경되는데, 타원외접사각형의한변의길이는 사이이다. 외곽선과채우기속성은각기 CPen 객체와 CBrush 객체를사용하여무작위로변경하여설정한다. 설정된속성들은변경될때까지이함수안에서사각형을그릴때와 OnMouseMove() 에서선분을그릴때적용된다. m_nshape가 2일경우에는마우스이동위치들을연결하게되는데, 이함수에서는시작위치가되는현재의좌표를 m_pointold에저장만하며, 그리기출력은하지않는다. 이함수의끝에서는 m_bmousedown의값을 TRUE로지정하여마우스왼쪽버튼이눌려져있음을나타낸다. OnLButtonUp() 함수는그리기동작의완료를위한작업을수행하는데, m_nshape의값을 -1 로하고 UpdateData(FALSE) 를수행함으로써라디오버튼선택을지우고, m_bmousedown의값을 FALSE로지정하여마우스왼쪽버튼이더이상눌려져있지않음을나타낸다. OnMouseMove() 함수는 Free Drawing의경우에만의미가있는데, 이전의마우스위치와현재마우스위치를연결하는선분을그린다. 선분을그리기위한조건은 m_nshape == 2(Free Drawing 선택 ) 와 m_bmousedown == TRUE( 마우스왼쪽버튼이눌려져있음 ) 두가지모두충족되어야한다. 선분을그리기위해사용하는함수는시작점으로이동하기위한 MoveTo() 와실제선분을그리는 LineTo() 이다. 선분을그린후에는현재의위치가 m_pointold에저장된다
137 CDC *pdc = 0; void CGraphicsDlg::OnLButtonDown(UINT nflags, CPoint point) { // TODO: Add your message handler code here and/or call default static CClientDC dc(this); pdc = &dc; UpdateData(); if (m_nshape == 0) { dc.rectangle(point.x, point.y, point.x + 20, point.y + 20); else if (m_nshape == 1) { CPen pen; pen.createpen(ps_dot, 1 + rand()%5, RGB(rand()%256, rand()%256, rand()%256)); dc.selectobject(&pen); CBrush brush; int r = rand() % 7; int hatch_style =!(r - 1) * HS_BDIAGONAL +!(r - 2) * HS_CROSS +!(r - 3) * HS_DIAGCROSS +!(r - 4) * HS_FDIAGONAL +!(r - 5) * HS_HORIZONTAL +!(r - 6) * HS_VERTICAL; if (r == 0) brush.createsolidbrush(rgb(rand()%256, rand()%256, rand()%256)); else brush.createhatchbrush(hatch_style, RGB(rand()%256, rand()%256, rand()%256)); dc.selectobject (&brush); dc.ellipse(point.x, point.y, point.x rand()%80, point.y rand()%80); else if (m_nshape == 2) { m_pointold = point; m_bmousedown = TRUE; CDialogEx::OnLButtonDown(nFlags, point);
138 void CGraphicsDlg::OnLButtonUp(UINT nflags, CPoint point) { // TODO: Add your message handler code here and/or call default m_nshape = -1; m_bmousedown = FALSE; UpdateData(FALSE); CDialogEx::OnLButtonUp(nFlags, point); void CGraphicsDlg::OnMouseMove(UINT nflags, CPoint point) { // TODO: Add your message handler code here and/or call default if (m_nshape == 2 && m_bmousedown) { pdc->moveto(m_pointold); pdc->lineto(point); m_pointold = point; CDialogEx::OnMouseMove(nFlags, point); 연습문제 1. 창 (window) 안의클라이언트영역에굵은적색원을그리기위해필요한문장들을나열하라. 원의크기와테두리선의굵기는임의로정하고, 원을그릴창객체를가리키는포인터는 p_win 이라고하자. 2. (1) void CGraphicsDlg::OnMouseMove(UINT nflags, CPoint point) 등의함수에서파라미터 nflags에대해조사하라. (2) 이절의예제프로그램에서멤버변수 m_bmousedown을사용하는대신 nflags를이용하는형태로프로그램을변경하라
139 5 장 SDI 응용프로그램 5.1 문서 / 뷰구조와 SDI 응용프로그램 문서 / 뷰구조 SDI 응용프로그램유형또는 MDI 응용프로그램유형이선택될경우응용프로그램마법사 (AppWizard) 가생성하는프로그램구조는문서 / 뷰 (Document/View) 구조를중심으로이루어진다. 이러한응용프로그램들은문서의전체또는일부를메모리에읽어들인후그내용을어떤형태로든사용자에게보여준다. 이렇게문서의내용을화면을통해사용자에게제시하는것을뷰라고한다. 응용프로그램들중에는하나의문서에대해한가지뷰만제공하는경우도있고여러가지뷰를제공하는경우도있다. 문서를취급하는응용프로그램들중에는한번에한개의문서만을다루는경우도있고동시에여러개의문서를열어둔상태에서작업을수행하는경우도있다. 여러개의문서를동시에다루는경우문서유형은한가지인경우도있고서로다른여러유형의문서들을취급하는경우도있다. 메모장의경우한번에한개의문서만취급하며다루는문서유형도텍스트파일로한정된경우이다. 또뷰의형태도한가지로정해져있다. 웹브라우저의경우일반뷰에서는 HTML 문서를해석한다음출력형식을조정하여사용자가보기좋게보여준다. 또대부분의웹브라우저들은 HTML 문서의소스를텍스트파일로보여주는뷰도제공한다. 또 MS Excel의경우테이블형태로작성된문서에대해히스토그램이나파이차트등여러가지뷰를제공하기도한다. MS 워드의경우입력파일이나문서저장형식은여러종류이다. 그러나 MS 워드프로그램에서작업중인문서형식은 MS 워드문서라는한가지유형이다. 반면에 Visual C++ 의경우, 프로그램소스를나타내는텍스트파일, 아이콘들을나타내기위한이미지파일, 클래스마법사를위한.clw 파일등다양한유형의파일들을동시에열어두고사용한다. 응용프로그램에서문서가나타내는측면은주로파일에저장하거나파일로부터읽어들이는등의외부저장매체와연관된측면인데비해, 뷰의경우는문서의내용을사용자에보여주는측면을담당하고있다. 이러한기능을분리하고있는가장중요한이유는하나의문서에대응되는뷰가둘이상인경우도있기때문이다. 이와같이문서와뷰로이루어지는프로그램구조를지원하기위한클래스로 MFC는 CDocument와 CView를제공하고있다. CDocument 클래스는문서를메모리상에서나타내기위한자료구조의표현과문서의저장및읽어들이기등을주로담당한다. ( 도큐먼트클래스안의자료구조가문서에대응되는파일의데이터전부를메모리안에갖고있을필요는없으며, 특히대용량파일의경우이는불가능하기도하다. 현재뷰가필요로하는문서부분만메모리안에갖고있을수도있고, 또는파일에접근하기위해필요한정보만갖고있을수도있다.) 반면 CView 클래스는문서의내용을화면상에서사용자에게보여주는일과문서에대한
140 사용자의편집행위를문서에반영하는일등을담당한다. 즉, 문서와사용자사이의인터페이스를담당하는것이뷰라고할수있으며, 이러한인터페이스는양방향으로이루어진다. SDI/MDI 응용프로그램유형을선택하면 AppWizard는 CDocument에서파생된클래스와 CView에서파생된클래스를만들어주며, 이러한파생클래스들에필요한멤버들을추가하거나편집하여응용프로그램을만들어나간다. 오른쪽그림은앞에서설명된문서클래스와뷰클래스의관계와이들클래스의기능을요약해보이고있다. 문서클래스는디스크에서읽어들이거나디스크에저장하는기능이필요할때는대체로 Serialize() 함수를사용하도록되어있다. 뷰클래스에서문서클래스의데이터에접근할경우에는잘정의된문서클래스의멤버함수를통해서접근하는것이바람직한방법이다. 뷰클래스는문서클래스의데이터를받아와서화면에보여주고사용자이벤트및시스템이벤트등을처리하기위한코드를포함한다. SDI 응용프로그램의형태 AppWizard가생성하는 SDI 응용프로그램의주실행창을나타내는객체는 CFrameWnd에서파생된클래스인스턴스이며, 기본외형은오른쪽그림과같이메뉴와툴바를포함한다. 문서내용을보여주는데사용되는부분은이창의클라이언트영역이며, 이는 CView에서파생된클래스의인스턴스이다. SDI 응용프로그램의경우에는뷰와문서가 1:1 대응관계에있으며, 문서는대체로디스크에저장되는파일에대응된다
141 5.2 SDI 응용프로그램예제 이절에서다룰예제응용프로그램은행단위의텍스트입력기능을갖춘 SDI 응용프로그램이다. 편집메뉴의 " 추가 (A) Ctrl+A" 메뉴항목을선택하면텍스트입력다이얼로그가나타나며, 입력된행은문서의마지막행으로추가된다. 뷰창에는텍스트행들앞에행번호를붙여표시하는데, 최대 5행까지만표시한다. 행추가기능은메뉴항목을통해서도사용할수있지만단축키 "Ctrl+A" 또는툴바의버튼을통해사용할수도있다. 이응용프로그램은문서의저장, 열기, 인쇄, 인쇄미리보기등의일반파일기능들도지원한다. 또상 / 하화살표키들을이용하여문서의앞쪽또는뒤쪽으로이동할수있다. 프로젝트생성단계 프로젝트이름으로는 "SDI" 를사용하기로한다. 응용프로그램유형은 < 단일문서 >, 프로젝트스타일은 <MFC 표준 > 을선택한다. 나머지대부분의옵션들은변경할필요는없다. 이예제에서는리소스언어로한국어를선택한다. [ 문서템플릿속성 ] 선택화면은문서를저장할때사용하는확장자와파일열기 / 저장다이얼로그나뷰창등에서문서유형을표시하는스트링들을지정하기위한것이다. 문서를취급하는응용프로그램들은흔히가장최근에사용된몇개의파일에대한목록을유지한다. [ 고급기능 ] 선택화면끝의스핀컨트롤은이응용프로그램에서유지할
142 목록의크기를지정하기위해사용되는데, 기본값은 4 이다. 생성된클래스들을보여주는화면은다음과같다
143 위의화면에따르면 AppWizard가생성하는클래스는 4개이며, 각기응용프로그램, 주실행창, 문서, 뷰등을나타내기위한것이다. 이들클래스는각각 CWinAppEx, CFrameWnd, CDocument, CView 등에서파생된클래스이며, 이름은각기 CxxxApp, CMainFrame, CxxxDoc, CxxxView와같이정해지는데, 여기서 xxx는프로젝트이름을나타낸다. 대화상자기반응용프로그램을선택하면 AppWizard는다이얼로그, 아이콘, 스트링테이블, 버전정보등의 4가지리소스를기본적으로생성한다. SDI/MDI 응용프로그램의경우에도이 4가지리소스가포함되며, 여기에메뉴, 단축키 (accelerator), 툴바등 3가지리소스가추가로생성된다. SDI/MDI 응용프로그램에서는메뉴제공이필수이며, 단축키와툴바도자주사용되는메뉴항목에대한접근을용이하게하기위한방법으로제공된다. CxxxDoc 클래스관련작업 도큐먼트클래스에서일반적으로코딩이필요한사항은문서데이터를메모리에서나타내기위해필요한자료구조의정의, 자료구조의초기화, 문서의저장 / 열기를처리하는 Serialize() 함수편집등이다. 도큐먼트클래스안의자료구조가 private 가시성을갖도록선언될경우에는뷰클래스에서문서클래스자료구조에접근할수있도록필요한인터페이스함수들을정의해주어야할것이지만, 이예제에서는자료구조를 public 가시성을갖도록선언하고있다. 이는물론객체지향프로그래밍의정보은닉원리에위배되며바람직하지않은것으로평가되지만, 편리성으로인해많이이용되고있다. 1. 여기에서는텍스트행들을저장하는방법으로크기가 100인 CString 배열을사용한다. 따라서최대 100 행까지입력받을수있다. 또실제입력된행의수를표시하기위한정수형변수가필요하다. 따라서다음과같은변수들을 public 가시성을갖도록선언한다. CString int m_strlines[100]; m_nlines; 2. CxxxDoc::OnNewDocument() 함수를편집하여초기화작업을표시한다. 이함수는문서초기화를담당하는데, 프로그램이시작될때와새파일메뉴항목이선택될때호출되어실행된다. 여기에서는현재입력된텍스트가없음을나타내기위해아래문장을포함시킨다
144 m_nlines = 0; 3. CxxxDoc::Serialize(CArchive &ar) 함수에파일저장및열기를위한코드를아래와같이만들어넣는다. 이함수는 CArchive 객체참조를파라미터로받는데, CArchive는파일에대한입출력을 << 연산자와 >> 연산자를사용하여쉽게표현할수있게해주기위한클래스이다. CArchive 객체는파일 (CFile) 객체에연계되어있으며, 현재저장용으로사용될것인지열기용으로사용될것인지에대한정보를갖고있다. 이객체에 IsStoring() 또는 IsLoading() 함수를적용시켜용도를확인할수있다. CArchive 객체가나타내는파일은파일에대한열기, 저장, 다른이름으로저장등의메뉴항목의처리과정에서정해진다. if (ar.isstoring()) { // 저장작업코드 ar << m_nlines; for (int i = 0; i < m_nlines; i++) ar << m_strlines[i]; else { // 열기작업코드 ar >> m_nlines; for (int i = 0; i < m_nlines; i++) ar >> m_strlines[i]; 참고사항 : 직렬화 (serialization) 직렬화란프로그램안의객체를디스크파일과같은외부저장매체에출력하거나혹은매체로부터입력받는프로세스를말한다. C++ 프로그램안의객체의데이터부분을나타내는구조체안에는포인터만몇개들어있고, 실제데이터는포인터가가리키는별도의주소에들어있을수도있다. 따라서구조체자체가차지하는메모리부분에들어있는값을출력하는것은무의미할수도있으며, 나중에다시읽었을때의미있는데이터가되기위한출력방법은단순하지않을수도있다. 프로그램안의객체가실제로나타내는데이터는포인터들에의해연결된 2차원적인구조이기쉬우며, 이데이터를의미있게정리하여출력한결과는일차원적인바이트열이다. 직렬화라는단어는객체출력의바로이러한점을잘표현하고있다. 직렬화에의해저장된데이터를다시읽어들여객체를구성하는것은직렬화의반대과정이며, 이를역직렬화 (de-serialization) 라고한다. CxxxView 클래스관련작업 뷰클래스에서도멤버변수의선언및초기화가필요할것이다. 초기화는생성자함수와 OnInitialUpdate() 함수등에서이루어질수있는데, OnInitialUpdate() 함수를이용할경우에는
145 뷰클래스에대해가상함수추가절차가필요하다. 뷰클래스에서가장본질적인기능이라면문서의내용을사용자에게보여주는것이며, 이를위해도큐먼트객체에접근할필요가있다. 뷰클래스안에는연계되어있는도큐먼트객체에대한포인터를리턴해주는 GetDocument() 라는함수가정의되어있으며, 이러한경우에이용된다. 문서내용을화면에표시하는코드는흔히뷰클래스의멤버함수 OnDraw() 안에들어간다. 창의생성시, 또는창의가려졌던부분이다시전면에나타날때, WM_PAINT 메시지가발생하며이메시지는 CWnd::OnPaint() 라는함수에의해처리된다. 이함수는많은경우스스로이작업을처리하기보다뷰클래스의 OnDraw() 함수를호출하여화면표시작업을맡긴다. OnPaint() 함수가 OnDraw() 함수를사용하는이유는인쇄나인쇄미리보기등의메뉴항목을처리하는 OnPrint() 함수가동일한작업을필요로하기때문에공통적인코드를 OnDraw() 함수로만들어두고 OnPaint() 함수와 OnPrint() 함수등에서호출하여사용할수있도록하기위함이다, OnDraw() 함수는 CDC* 타입의파라미터를취하는데, 이파라미터는페인트가필요한화면영역이나프린터를나타내는 DC 객체를가리키는포인터이다. 뷰클래스에서는또한사용자의문서편집작업이나문서안에서의위치이동등의상호작용을처리하기위한메시지처리기함수들을만들어주어야한다. 이상의작업들을하나씩처리해보자. 1. 문서의여러텍스트행중현재어느부분을화면에보여줄지를나타내기위해화면에나타나는첫번째행번호를표시하는정수형멤버변수 m_npos 를둔다. 또예제프로그램에서는화면에최대다섯행까지의텍스트만을보여주며, 이뷰의크기는변경되지는않는다. 그러나추후의확장성을위해뷰의크기를나타내는정수형변수 m_nvusz를둔다. 이변수들을 private 가시성을갖도록다음과같이선언한다. int m_npos; // 화면상첫행의인덱스, 0 <= m_npos < m_nlines ( 도큐먼트크기 ) int m_nvusz; // 뷰의높이 ( 값은 5로초기화한다 ) 2. 생성자 CxxxView() 안에서 m_nvusz 를 5 로초기화한다. m_nvusz = 5; 3. 클래스마법사를불러 [ 가상함수 ] 탭에서가상함수추가절차를사용하여뷰클래스에 CxxxView::OnInitialUpdate() 함수를추가하고, 그안에서 m_npos를 0으로초기화한다. m_npos = 0; 4. 뷰의내용을그려내기위해호출되는 CxxxView::OnDraw() 함수는아래와같은모양으로만들어져있다. 이함수는먼저 GetDocument() 함수를호출하여이뷰에연계되어있는문서객체를가리키는포인터를받아온다. 받아온포인터의유효성을점검하기위해
146 ASSERT_VALID 마크로를사용한다. void CSDIView::OnDraw(CDC* pdc) { CSDIDoc* pdoc = GetDocument(); ASSERT_VALID(pDoc); if (!pdoc) return; // TODO: 여기에원시데이터에대한그리기코드를추가합니다. 출력대상텍스트행의인덱스는 m_npos에서시작하여 m_npos + (m_nvusz - 1) 까지이다. 그러나이인덱스범위가도큐먼트텍스트마지막행을지나칠경우도큐먼트텍스트마지막행까지만출력한다. 각행의앞에는 행인덱스 : 를덧붙여문서상에서의행위치를표시한다. CString::Format() 함수는 C 표준함수 sprintf() 와유사한방식으로동작하여출력결과가스트링객체의값이된다. CDC::TextOut() 함수에서각행출력위치는 (10, 10), (10, 30), (10, 50),... 등과같이정한다. OnDraw() 함수의 // TODO: 아래에들어가는코드는다음과같다. for (int i = m_npos; i < m_npos + m_nvusz && i < pdoc->m_nlines; i++) { CString str; str.format(_t("%d : "), i); pdc->textout(10, *(i - m_npos), str + pdoc->m_strlines[i]); 텍스트입력다이얼로그의추가 [ 편집 ] 메뉴의 [ 리소스추가 ] 메뉴항목을선택한다음리소스형식으로는 Dialog를선택하고 < 새로만들기 > 버튼을누른다. 다이얼로그안에는 < 확인 > 버튼, < 취소 > 버튼, Static text, Edit Box (m_strline 변수 ) 등을둔다. 버튼들은다이얼로그안에기본으로포함되어있는것을지우지않고남겨두는방법으로포함시킨다. 이점은중요한데, 이는이버튼들에연결되어있는 CDialog::OnOK() 함수와 CDialog::OnCancel() 함수를이용할필요가있기때문이다. 만일이버튼들을새로추가하여캡션들을 " 확인 ", " 취소 " 등으로만들어주었다면여기에메시지처리기함수들을만들고그안에서 OnOK() 와
147 OnCancel() 함수를호출해주어야한다. 이러한복잡한일을피하기위해서는원래의버튼들을살려두어야한다. 설계된다이얼로그리소스들은대체로프로그램에서 modal 다이얼로그로사용된다. 이응용프로그램의경우에는 " 추가 (A) Ctrl+A" 메뉴항목을선택하게되면, 이메뉴항목의처리기함수안에서위의다이얼로그를생성하여화면에나타나게만들것이다. 이를위해서는다이얼로그객체를생성하여여기에 CDialog::DoModal() 함수를호출하여야하는데, 객체의생성을위해서는위의다이얼로그를나타내는클래스가필요하다. ( 참고 : modeless 다이얼로그의경우에는 DoModal() 함수대신 Create() 함수가사용되며, 추가로고려해야할사항들이많다.) 다이얼로그리소스가활성화되어있는상태에서, 즉, 다이얼로그편집화면상태에서팝업메뉴를불러 [ 클래스추가 ] 메뉴항목을선택하면아래그림과같은클래스추가마법사가나타난다. 이때클래스이름으로 CInputDialog를입력하면, 이클래스의정의및구현을위한파일이름으로 InputDialog.h, InputDialog.cpp 등이정해진다. 대화상자 ID는위에서만든다이얼로그 ID이다. 행추가메뉴항목 1. 메뉴리소스를편집하여행추가를위한메뉴항목을포함시킨다. SDI 응용프로그램에서는이미메뉴리소스가포함되어있어필요한메뉴나메뉴항목을
148 쉽게추가할수있다. 먼저프로젝트워크스페이스다이얼로그에서리소스뷰탭을클릭한다. 메뉴리소스왼쪽의 아이콘을클릭하여메뉴리소스를펼치면, 주실행창을위한메뉴리소스가나타난다. ( 레이블 : IDR_MAINFRAME) 위의 IDR_MAINFRAME 메뉴리소스를더블클릭하여메뉴를펼친다음 " 편집 (E)" 메뉴끝에분리선을추가한다음행추가를위한메뉴항목 " 추가 (A) Ctrl+A" 을추가한다. 이메뉴항목의 ID는다른메뉴항목 ID들과형식을맞추기위해 ID_EDIT_APPEND로정한다. 메뉴항목속성중 Prompt 속성은메뉴항목위에마우스커서를위치시켰을때주실행창의상태표시줄에나타날메뉴항목에대한간단한설명이다. Prompt 속성값끝의 n 이후부분은툴팁 (tooltip) 이다. 2. ID_EDIT_APPEND 메뉴항목을위한처리기함수 OnEditAppend() 를뷰클래스, 즉, CSDIView 클래스안에추가한다. 이를위해리소스뷰에서 [ 추가 ] 메뉴항목위에팝업메뉴를부른다음 [ 이벤트처리기 ] 메뉴항목을선택하면아래화면과같은 [ 이벤트처리기마법사 ] 화면이나타난다. ( 클래스마법사를통해이벤트처리기를생성할수도있다.) 아래화면에서 < 메시지형식 > 에는 COMMAND, < 클래스목록 > 에는뷰클래스를선택한다. 실제로메뉴항목처리기함수는아무클래스에나만들어넣을수있다, 그러나텍스트행추가와같은문서편집작업은그결과가뷰에도반영되어야하며또문서에도반영되어야한다. 뷰와문서데이터모두에가장손쉽게접근할수있는곳이뷰클래스이며, 따라서문서편집관련처리기함수는뷰클래스안에정의하는것이바람직하다
149 위의화면에서 < 추가및편집 > 버튼을누른후아래코드를만들어넣는다. #include "InputDialog.h" void CSDIView::OnEditAppend() { CInputDialog idlg; if (idlg.domodal() == IDOK) { CSDIDoc* pdoc = GetDocument(); ASSERT_VALID(pDoc); pdoc->m_strlines[pdoc->m_nlines] = idlg.m_strline; pdoc->m_nlines++; pdoc->setmodifiedflag(true); m_npos = pdoc->m_nlines - m_nvusz; if (m_npos < 0) m_npos = 0; Invalidate();
150 OnEditAppend() 함수안에서먼저할일은텍스트행을받아들일다이얼로그박스를띄우는것이며, 이를위해 CInputDialog 객체 idlg를생성한다음이객체에대해 DoModal() 함수를호출한다. 텍스트행의추가는다이얼로그박스에서 OK 버튼을클릭하여닫아줄경우에만이루어져야하는데, 이는 DoModal() 함수의리턴값이 IDOK임을확인하면된다. 입력된텍스트는문서의끝에추가되어야하며, 이를위해먼저문서객체를가리키는포인터를받아와서 pdoc에저장한다. 문서에텍스트행을추가한다음 CDocument::SetModifiedFlag() 함수를호출하는데, 이는문서에변경이있었음을표시하는플래그를설정하기위한것이다. 나중에파일을저장하지않고프로그램을종료하려고하면, 문서에변경이있었음을알리고현재문서내용을저장할것인지묻게된다. 추가된텍스트를뷰에반영하는방법은여러가지가있으나이미만들어져있는 OnDraw() 함수를이용하는것이가장간단한방법이다. CWnd::Invalidate() 함수는클라이언트영역을대상으로 OnDraw() 함수를호출하는결과를초래하게되며, 이과정에서 OnDraw() 함수에전달될 DC 객체도적절히준비된다. 여기에서는먼저텍스트상의출력위치를나타내는 m_npos를적절히설정한다음 Invalidate() 함수를호출한다. AppWizard는응용프로그램유형에따라필요한여러클래스들을만들고이들을사용하는곳에클래스를정의하는헤더파일에대한 #include 명령들을적절하게포함시켜준다. 그러나 CInputDialog는 AppWizard가만든클래스가아니므로이클래스를정의하는헤더파일에대한 #include 명령을넣는것은프로그래머의책임이다. 이예제의경우이명령은 SDIView.h의임의의위치, SDIView.cpp에서 OnEditAppend() 함수가정의되기전의임의의위치에들어가면된다. 행추가메뉴항목을위한단축키 (accelerator) 설정 메뉴항목을사용하기위해서는메뉴를먼저펼친다음원하는메뉴항목을클릭해야하는데, 때로 Ctrl+F와같이특별한키조합을메뉴항목에연계시켜두고이키조합을사용하는것이효율적일수도있다. 이러한키조합을단축키라고부르며, 자주사용되는메뉴항목들에는대개단축키들이설정되어있는경우가많다. 단축키는 Accelerator 리소스를통해설정된다. 아래그림과같이 Accelerator 리소스맨끝에는새로운단축키를설정할수있도록비어있는행이주어져있다. 이행을클릭한후 [ 속성 ] 창에서 ID 속성과동작속성들을설정한다
151 동작속성들은단축키로사용될키조합을표시하는데, 키조합표시방법을모를경우에는선택된단축키리소스행에대해팝업메뉴를부른후 [ 다음입력된키 ] 메뉴항목을선택한다. 옆의그림과같은화면이나타날텐데, 이때단축키로사용될키조합을누르면동작속성들에적절한값이들어간다. 단축키가메뉴항목과동일한작업을수행하도록만들기위해필요한것은단축키를위한 ID와메뉴항목의 ID를일치시켜주는것이며, 이경우단축키를위한별도의처리기함수를만들필요는없다. 앞에서메뉴항목의캡션을 " 추가 (A) Ctrl+A" 와같이지정하였었는데, 캡션에포함된 Ctrl+A는사용자에게단축키를알려주는설명일뿐기능적의미는전혀없다. Ctrl+A가실제로이메뉴항목을위한단축키로작용하기위해서는지금기술한바와같이단축키리소스에서필요한설정을거쳐야한다. 행추가메뉴항목을위한툴바 (Toolbar) 버튼설정 특히자주사용되는메뉴항목들은대개메뉴아래에위치한툴바 (Toolbar) 에버튼으로등록되어있어메뉴에서보다쉽게이용할수있도록만들어져있다. 툴바버튼은툴바리소스를통해설정된다. 아래그림과같이 Toolbar 리소스맨끝에는새로운버튼을설정할수있도록비어있는버튼이주어져있다. 이버튼을편집할수있도록이미지편집기가제공되며, 여기에서 Visual Studio 하단툴바의그리기도구들과화면오른쪽색상선택창을사용하여버튼이미지를그린다. 또속성창에서해당버튼의 ID를메뉴항목의 ID와일치시켜주는것만으로메뉴항목기능과의연계가이루어진다
152 화살표키 (UP) 와 (DOWN) 의기능설정 화살표키들은단축키로서기능한다. 따라서 Accelerator 리소스를통해설정할수있다. 이키들에대한키값은 VK_UP과 VK_DOWN이다. 이키들이갖는기능은메뉴항목에는없으며, 따라서새로운 ID를설정해야하는데, 여기에서는각기 ID_ARROW_UP, ID_ARROW_DOWN 등으로정한다
153 이키들은뷰에서보여주는문서상의텍스트위치를한행위로또는한행아래로이동시키기위해사용된다. 이는 ID_ARROW_UP, ID_ARROW_DOWN 등의 ID들에대해각기 COMMAND 메시지처리기를만들어줌으로써해결될수있다. 이함수들도뷰클래스안에두는것이바람직하다. 이함수들을만들기위해서도이벤트처리기마법사또는클래스마법사를사용할수있다. void CSDIView::OnArrowDown() { CSDIDoc* pdoc = GetDocument(); ASSERT_VALID(pDoc); (m_npos < pdoc->m_nlines - 1)? m_npos++ : 0; Invalidate(); void CSDIView::OnArrowUp() { (m_npos > 0)? m_npos-- : 0; Invalidate(); 연습문제 본문예제에관련된다음질문들에답하라. 1. CSDIDoc 클래스에서문서데이터를표현하기위해사용된 m_strlines 배열과 m_nlines 등의가시성을 private로선언하고이에따라 CSDIDoc 클래스와 CSDIView 클래스를고쳐라. ( 단, friend 메커니즘은사용하지않는다.) 2. 이장에서는텍스트행들의저장을위해고정크기의스트링배열을사용하고있다. 스트링배열대신 char 배열안에텍스트행들을저장하는방식으로프로그램을재작성하라. 이러한변경이원래의프로그램에대해이루어지는경우와위의 1번문제에서수정된프로그램에대해이루어지는경우를비교하여논하라. 3. CStringArray는스트링들을저장하는배열을나타내는 MFC 클래스이며, 저장될스트링들이많아지면자바의 Vector 클래스처럼크기가자동적으로늘어난다. char 배열대신 CStringArray 객체를사용하여앞의 2번문제에대해답하라
154 4. CSDIDoc 클래스에서 m_nlines의초기화를생성자에서수행하지않고 OnNewDocument() 함수안에서수행하고있다. 이초기화를생성자에서수행할경우어떤문제점이생길지조사하고그이유에대해논하라. 5. CSDIView 클래스의경우초기화문장들을생성자안에서처리하는경우와 OnInitialUpdate() 함수안에서처리하는경우사이에실행상가시적차이를관찰할수있는지에대해실험하고논하라. 6. 이프로그램의뷰는화면이가려졌다나타날때에도원래의내용을유지한다. 그이유는무엇인가? 7. 메뉴항목과 accelerator 가동일한기능을갖도록만드는방법을설명하라. 8. 다이얼로그기반의응용프로그램에는포함되지않지만 SDI/MDI 응용프로그램에는자동으로포함되는리소스 3 가지는무엇인가? 9. SDI 응용프로그램을작성할때, 프로그래머가도큐먼트클래스와뷰클래스에포함시키는요소들을기술하라. 프로그램소스 ( 일부 ) InputDialog.h InputDialog.cpp SDIDoc.h SDIDoc.cpp SDIView.h SDIView.cpp ================================================================================= // InputDialog.h : header file #pragma once // CInputDialog 대화상자입니다. class CInputDialog : public CDialogEx { DECLARE_DYNAMIC(CInputDialog) public:
155 CInputDialog(CWnd* pparent = NULL); // 표준생성자입니다. virtual ~CInputDialog(); // 대화상자데이터입니다. enum { IDD = IDD_DIALOG1 ; protected: virtual void DoDataExchange(CDataExchange* pdx); // DDX/DDV 지원입니다. public: ; DECLARE_MESSAGE_MAP() CString m_strline; ================================================================================= // InputDialog.cpp : 구현파일입니다. // #include "stdafx.h" #include "SDI.h" #include "InputDialog.h" #include "afxdialogex.h" // CInputDialog 대화상자입니다. IMPLEMENT_DYNAMIC(CInputDialog, CDialogEx) CInputDialog::CInputDialog(CWnd* pparent /*=NULL*/) : CDialogEx(CInputDialog::IDD, pparent), m_strline(_t("")) { CInputDialog::~CInputDialog() { void CInputDialog::DoDataExchange(CDataExchange* pdx) { CDialogEx::DoDataExchange(pDX); DDX_Text(pDX, IDC_EDIT1, m_strline); BEGIN_MESSAGE_MAP(CInputDialog, CDialogEx) END_MESSAGE_MAP() // CInputDialog 메시지처리기입니다
156 ================================================================================= // SDIDoc.h : CSDIDoc 클래스의인터페이스 // #pragma once class CSDIDoc : public CDocument { protected: // serialization에서만만들어집니다. CSDIDoc(); DECLARE_DYNCREATE(CSDIDoc) // 특성입니다. public: CString m_strlines[100]; int m_nlines; // 작업입니다. public: // 재정의입니다. public: virtual BOOL OnNewDocument(); virtual void Serialize(CArchive& ar); #ifdef SHARED_HANDLERS virtual void InitializeSearchContent(); virtual void OnDrawThumbnail(CDC& dc, LPRECT lprcbounds); #endif // SHARED_HANDLERS // 구현입니다. public: virtual ~CSDIDoc(); #ifdef _DEBUG virtual void AssertValid() const; virtual void Dump(CDumpContext& dc) const; #endif protected: // 생성된메시지맵함수 protected: DECLARE_MESSAGE_MAP()
157 #ifdef SHARED_HANDLERS // 검색처리기에대한검색콘텐츠를설정하는도우미함수 void SetSearchContent(const CString& value); #endif // SHARED_HANDLERS ; ================================================================================= // SDIDoc.cpp : CSDIDoc 클래스의구현 // #include "stdafx.h" // SHARED_HANDLERS는미리보기, 축소판그림및검색필터처리기를구현하는 // ATL 프로젝트에서정의할수있으며해당프로젝트와문서코드를공유하도록해줍니다. #ifndef SHARED_HANDLERS #include "SDI.h" #endif #include "SDIDoc.h" #include <propkey.h> #ifdef _DEBUG #define new DEBUG_NEW #endif // CSDIDoc IMPLEMENT_DYNCREATE(CSDIDoc, CDocument) BEGIN_MESSAGE_MAP(CSDIDoc, CDocument) END_MESSAGE_MAP() // CSDIDoc 생성 / 소멸 CSDIDoc::CSDIDoc() { // TODO: 여기에일회성생성코드를추가합니다. CSDIDoc::~CSDIDoc() {
158 BOOL CSDIDoc::OnNewDocument() { if (!CDocument::OnNewDocument()) return FALSE; // TODO: 여기에재초기화코드를추가합니다. // SDI 문서는이문서를다시사용합니다. m_nlines = 0; return TRUE; // CSDIDoc serialization void CSDIDoc::Serialize(CArchive& ar) { if (ar.isstoring()) { // 저장작업코드 ar << m_nlines; for (int i = 0; i < m_nlines; i++) ar << m_strlines[i]; else { // 열기작업코드 ar >> m_nlines; for (int i = 0; i < m_nlines; i++) ar >> m_strlines[i]; #ifdef SHARED_HANDLERS // 축소판그림을지원합니다. void CSDIDoc::OnDrawThumbnail(CDC& dc, LPRECT lprcbounds) { // 문서의데이터를그리려면이코드를수정하십시오. dc.fillsolidrect(lprcbounds, RGB(255, 255, 255)); CString strtext = _T("TODO: implement thumbnail drawing here"); LOGFONT lf; CFont* pdefaultguifont = CFont::FromHandle((HFONT) GetStockObject(DEFAULT_GUI_FONT)); pdefaultguifont->getlogfont(&lf); lf.lfheight = 36; CFont fontdraw;
159 fontdraw.createfontindirect(&lf); CFont* poldfont = dc.selectobject(&fontdraw); dc.drawtext(strtext, lprcbounds, DT_CENTER DT_WORDBREAK); dc.selectobject(poldfont); // 검색처리기를지원합니다. void CSDIDoc::InitializeSearchContent() { CString strsearchcontent; // 문서의데이터에서검색콘텐츠를설정합니다. // 콘텐츠부분은 ";" 로구분되어야합니다. // 예 : strsearchcontent = _T("point;rectangle;circle;ole object;"); SetSearchContent(strSearchContent); void CSDIDoc::SetSearchContent(const CString& value) { if (value.isempty()) { RemoveChunk(PKEY_Search_Contents.fmtid, PKEY_Search_Contents.pid); else { CMFCFilterChunkValueImpl *pchunk = NULL; ATLTRY(pChunk = new CMFCFilterChunkValueImpl); if (pchunk!= NULL) { pchunk->settextvalue(pkey_search_contents, value, CHUNK_TEXT); SetChunkValue(pChunk); #endif // SHARED_HANDLERS // CSDIDoc 진단 #ifdef _DEBUG void CSDIDoc::AssertValid() const { CDocument::AssertValid();
160 void CSDIDoc::Dump(CDumpContext& dc) const { CDocument::Dump(dc); #endif //_DEBUG // CSDIDoc 명령 ================================================================================= // SDIView.h : CSDIView 클래스의인터페이스 // #pragma once class CSDIView : public CView { protected: // serialization에서만만들어집니다. CSDIView(); DECLARE_DYNCREATE(CSDIView) // 특성입니다. public: CSDIDoc* GetDocument() const; // 작업입니다. public: // 재정의입니다. public: virtual void OnDraw(CDC* pdc); // 이뷰를그리기위해재정의되었습니다. virtual BOOL PreCreateWindow(CREATESTRUCT& cs); protected: virtual BOOL OnPreparePrinting(CPrintInfo* pinfo); virtual void OnBeginPrinting(CDC* pdc, CPrintInfo* pinfo); virtual void OnEndPrinting(CDC* pdc, CPrintInfo* pinfo); // 구현입니다. public: virtual ~CSDIView(); #ifdef _DEBUG
161 #endif virtual void AssertValid() const; virtual void Dump(CDumpContext& dc) const; protected: // 생성된메시지맵함수 protected: afx_msg void OnFilePrintPreview(); afx_msg void OnRButtonUp(UINT nflags, CPoint point); afx_msg void OnContextMenu(CWnd* pwnd, CPoint point); DECLARE_MESSAGE_MAP() private: int m_npos; int m_nvusz; public: virtual void OnInitialUpdate(); afx_msg void OnEditAppend(); afx_msg void OnArrowUp(); afx_msg void OnArrowDown(); ; #ifndef _DEBUG // SDIView.cpp의디버그버전 inline CSDIDoc* CSDIView::GetDocument() const { return reinterpret_cast<csdidoc*>(m_pdocument); #endif ================================================================================= // SDIView.cpp : CSDIView 클래스의구현 // #include "stdafx.h" // SHARED_HANDLERS는미리보기, 축소판그림및검색필터처리기를구현하는 // ATL 프로젝트에서정의할수있으며해당프로젝트와문서코드를공유하도록해줍니다. #ifndef SHARED_HANDLERS #include "SDI.h" #endif #include "SDIDoc.h" #include "SDIView.h" #ifdef _DEBUG #define new DEBUG_NEW
162 #endif // CSDIView IMPLEMENT_DYNCREATE(CSDIView, CView) BEGIN_MESSAGE_MAP(CSDIView, CView) // 표준인쇄명령입니다. ON_COMMAND(ID_FILE_PRINT, &CView::OnFilePrint) ON_COMMAND(ID_FILE_PRINT_DIRECT, &CView::OnFilePrint) ON_COMMAND(ID_FILE_PRINT_PREVIEW, &CSDIView::OnFilePrintPreview) ON_WM_CONTEXTMENU() ON_WM_RBUTTONUP() ON_COMMAND(ID_EDIT_APPEND, &CSDIView::OnEditAppend) ON_COMMAND(ID_ARROW_UP, &CSDIView::OnArrowUp) ON_COMMAND(ID_ARROW_DOWN, &CSDIView::OnArrowDown) END_MESSAGE_MAP() // CSDIView 생성 / 소멸 CSDIView::CSDIView() : m_npos(0) { // TODO: 여기에생성코드를추가합니다. m_nvusz = 5; CSDIView::~CSDIView() { BOOL CSDIView::PreCreateWindow(CREATESTRUCT& cs) { // TODO: CREATESTRUCT cs를수정하여여기에서 // Window 클래스또는스타일을수정합니다. return CView::PreCreateWindow(cs); // CSDIView 그리기 void CSDIView::OnDraw(CDC *pdc) { CSDIDoc* pdoc = GetDocument();
163 ASSERT_VALID(pDoc); if (!pdoc) return; // TODO: 여기에원시데이터에대한그리기코드를추가합니다. for (int i = m_npos; i < m_npos + m_nvusz && i < pdoc->m_nlines; i++) { CString str; str.format(_t("%d : "), i); pdc->textout(10, *(i - m_npos), str + pdoc->m_strlines[i]); // CSDIView 인쇄 void CSDIView::OnFilePrintPreview() { #ifndef SHARED_HANDLERS AFXPrintPreview(this); #endif BOOL CSDIView::OnPreparePrinting(CPrintInfo* pinfo) { // 기본적인준비 return DoPreparePrinting(pInfo); void CSDIView::OnBeginPrinting(CDC* /*pdc*/, CPrintInfo* /*pinfo*/) { // TODO: 인쇄하기전에추가초기화작업을추가합니다. void CSDIView::OnEndPrinting(CDC* /*pdc*/, CPrintInfo* /*pinfo*/) { // TODO: 인쇄후정리작업을추가합니다. void CSDIView::OnRButtonUp(UINT /* nflags */, CPoint point) { ClientToScreen(&point); OnContextMenu(this, point); void CSDIView::OnContextMenu(CWnd* /* pwnd */, CPoint point)
164 { #ifndef SHARED_HANDLERS theapp.getcontextmenumanager()->showpopupmenu(idr_popup_edit, point.x, point.y, this, TRUE); #endif // CSDIView 진단 #ifdef _DEBUG void CSDIView::AssertValid() const { CView::AssertValid(); void CSDIView::Dump(CDumpContext& dc) const { CView::Dump(dc); CSDIDoc* CSDIView::GetDocument() const // 디버그되지않은버전은인라인으로지정됩니다. { ASSERT(m_pDocument->IsKindOf(RUNTIME_CLASS(CSDIDoc))); return (CSDIDoc*)m_pDocument; #endif //_DEBUG // CSDIView 메시지처리기 void CSDIView::OnInitialUpdate() { CView::OnInitialUpdate(); // TODO: 여기에특수화된코드를추가및 / 또는기본클래스를호출합니다. m_npos = 0; #include "InputDialog.h" void CSDIView::OnEditAppend() { CInputDialog idlg; if (idlg.domodal() == IDOK) { CSDIDoc* pdoc = GetDocument();
165 ASSERT_VALID(pDoc); pdoc->m_strlines[pdoc->m_nlines] = idlg.m_strline; pdoc->m_nlines++; pdoc->setmodifiedflag(true); m_npos = pdoc->m_nlines - m_nvusz; if (m_npos < 0) m_npos = 0; Invalidate(); void CSDIView::OnArrowUp() { (m_npos > 0)? m_npos-- : 0; Invalidate(); void CSDIView::OnArrowDown() { CSDIDoc* pdoc = GetDocument(); ASSERT_VALID(pDoc); (m_npos < pdoc->m_nlines - 1)? m_npos++ : 0; Invalidate();
166 6 장 MDI 응용프로그램 MDI 응용프로그램의형태 응용프로그램마법사 (AppWizard) 가생성하는 MDI 응용프로그램의주실행창을나타내는객체는 CFrameWnd에서파생된클래스인스턴스이며, 기본외형은아래그림과같이메뉴와툴바를포함한다. 문서내용을보여주는뷰들은이창의클라이언트영역안에자식창으로만들어지는윈도우들을통해표시된다. 주실행창의클라이언트영역안에는여러개의자식창이만들어질수있으며, 한개의문서에대응되는뷰가여러개일수도있다. 이점을제외하면 MDI 응용프로그램의형태는 SDI 응용프로그램의형태와거의같다. 이장에서는먼저 AppWizard, 클래스마법사 (ClassWizard), 리소스편집기등의도구들에대해다시요약하여설명하고, 특히 AppWizard를통해만들어지는것이무엇인지살펴본다. 그런다음예제로서간단한그림편집기응용프로그램을만들어본다. 하나의문서에대응되는둘이상의뷰가있을경우이들이항상동일한내용을나타낼수있도록동기화 (synchronization) 시켜주는방법을알아본다. 끝으로특정기능을사용하는뷰클래스를기본클래스로이용하는응용프로그램개발예를살펴본다
167 6.1 AppWizard, 클래스마법사, 리소스편집기 Visual C++ 개발환경안에는소프트웨어개발속도를크게향상시킬수있는여러가지도구들이포함되어있는데, 특히응용프로그램위저드 (AppWizard), 클래스마법사 (ClassWizard), 리소스편집기 (Resource Editor) 등이중요한도구들이다. 리소스편집기는각리소스유형의편집에사용되는여러편집기들의모음이다. AppWizard는프로그램틀생성기 (template generator) 라고볼수있다. 흔히새로운프로그램을작성할때기존의유사한프로그램소스에서불필요한부분들을제거한다음필요한기능들을추가하게될것이다. AppWizard는이중첫번째단계에해당하는작업을대신해주는도구이다. 즉, 새로작성하고자하는응용프로그램을위한 깨끗한 코드틀을만들어준다. AppWizard는개발자가선택한응용프로그램유형 (Dialog 기반, SDI, MDI), 여러선택항목등을반영하는프로그램소스를만들어준다. 응용프로그램의작성에서 AppWizard는시작부분에서단한번만사용된다는점을유념하자. AppWizard를사용한결과로여러파일들이만들어지며, 이파일들은컴파일되어실행될수있는완전한프로그램임을앞에서살펴본바있다. ClassWizard는 AppWizard가만들어준프로그램골격을수정하는데사용되는도구인데, 특히메시지맵의작성 / 변경, 새로운 MFC 파생클래스의생성, 멤버변수추가등의작업에주로이용된다. AppWizard에의해생성된프로그램골격에만적용될수있음을유념하자. 리소스는리소스편집기를사용하여생성하거나편집할수있는사용자인터페이스객체들이며, 여기에는비트맵, 커서, 다이얼로그, 아이콘, 메뉴, 단축키테이블, 스트링테이블, 버전정보등이포함된다. 리소스들이나타내는내용들은 MFC 코드를사용하여표현될수도있지만, 리소스편집기를사용함으로써훨씬쉽게이용할수있다. 6.2 AppWizard 의생성물 MDI 응용프로그램에서 AppWizard의진행절차는응용프로그램유형으로 Multiple Document를선택하는것을제외하면 SDI의경우와정확히같다. MDI 응용프로그램유형을선택하더라도실제취급하는문서유형은한가지인경우가많으며, 여러유형의문서를취급할수있기위해서필요한작업에대해서는 6.6절에서다룬다. 그렇지만한문서에대해여러개의뷰를사용할수있고또뷰를포함하는창이주실행창의클라이언트영역전체가아니라그안에포함된다는점등의차이가있다. MDI 응용프로그램의경우 AppWizard는 20개가넘는파일들을만들어낸다. 이파일들중의많은부분은프로그램소스를구성하는 C++ 헤더파일들과 C++ 클래스구현파일들이며, 이외에도여러리소스관련파일이나프로젝트관리와관련된파일들이있다. AppWizard 종료후프로젝트워크스페이스를통해클래스, 리소스, 파일등을살펴보면아래그림들과같다. MDI 응용프로그램의기본클래스들은 SDI 응용프로그램의기본클래스들과거의같으며,
168 단지 CChildFrame 클래스만이추가되어있다. 파일들의경우도 CChildFrame 클래스를위한헤더파일과클래스구현파일을제외하고는차이가없다. 리소스는둘다 7개의기본리소스를포함하는것으로정확히같다. 클래스뷰에서사용되는아이콘들의의미는다음과같다. - 클래스 - protected 멤버함수 - private 멤버함수 - public 멤버함수 - protected 멤버변수 - private 멤버변수 - public 멤버변수 AppWizard가생성하는 MDI 응용프로그램의기본클래스구조는아래그림과같다. 그림에서가장하단에위치한클래스들이실제로생성되는클래스들이며, 그위의클래스들은 MFC 계층구조안의클래스들이다
169 CObject CCmdTarget CWndThread CDocument CWnd CWinApp CFrameWnd CView CDialog CWinAppEx CMDIFrameWnd CMDIChildWnd CDialogEx CMDIFrameWndEx CMDIChildWndEx CMyApp CMyDoc CMainFrame CChildFrame CMyView CAboutDlg 6.3 간단한그림편집기의작성 예제프로그램은마우스드래깅 ( 왼쪽버튼을누른상태에서이동 ) 을따라 5x5 크기의정사각형을그린다. 이를위해프로젝트이름은 Drawing, 응용프로그램종류는 < 다중문서 >(< 탭문서 > 선택은해제 ), 리소스언어는 < 한국어 >, 프로젝트스타일은 <MFC 표준 > 등을선택한다. 마우스드래깅에반응하도록만들기위해뷰클래스안에 WM_MOUSEMOVE 메시지에대한처리기함수를만들고아래와같이정의한다. void CDrawingView::OnMouseMove(UINT nflags, CPoint point) { if (nflags == MK_LBUTTON) { // 마우스왼쪽버튼이눌려져있으면 CClientDC dc(this); dc.rectangle(point.x, point.y, point.x + 5, point.y + 5); CView::OnMouseMove(nFlags, point); OnMouseMove() 함수의첫번째파라미터는마우스버튼이나 CTRL/SHIFT 키상태를나타내며, 다음상태심볼들이정의되어있다. MK_CONTROL, MK_SHIFT, MK_LBUTTON, MK_MBUTTON, MK_RBUTTON
170 연습문제 1. 위의프로그램을컴파일하여실행시켜보라. 2. [ 창 ] 메뉴의 [ 새창 ] 메뉴항목은동일문서에대해뷰창을추가로만들기위해사용된다. 물론아직은문서를나타내기위한작업이없으므로위의프로그램의경우뷰들이별의미를갖지는않는다. 뷰의배열을 [ 창 ] 메뉴에서 [ 바둑판식배열 ] 을선택하여프로그램을수행해보라. 3. OnMouseMove() 함수안에서 dc 객체는매번새로만들어지는데이는낭비처럼생각될수있을것이다. 이러한낭비를피하기위해서는 static 선언을이용할수있다. dc 객체를 static으로선언한다음위의 2번문제에서와같이두개의뷰를연다음실험해보라. 이상한현상을관찰할수있을텐데, 그원인을설명해보라. 4. 위의 OnMouseMove() 함수를만들기위한방법이두가지있는데, 하나는 Visual Studio [ 프로젝트 ] 메뉴의 [ 클래스마법사 ] 메뉴항목을사용하는방법이며, 또하나는클래스뷰에대한팝업에서 [ 클래스마법사 ] 메뉴항목을사용하는것이다. 이들을각기사용해보고, 나타나는화면들을캡처하고설명을붙여제출하라. 5. 뷰가가려졌다다시노출될때가려졌던부분에위치한사각형들은사라진것을볼수있다. 풀다운메뉴에의해가려졌던부분은메뉴가사라진후어떻게되는지관찰하라. 관찰된결과의원인을설명하라. 앞의프로그램은문서파일을저장하거나읽어들이는기능, 가려졌다나타나는화면에대한재생기능이없는데, 이들은도큐먼트 / 뷰구조를사용하여해결될수있다. 도큐먼트 / 뷰구조를반영하기위한작업은다음 4가지이다. 1 사용자가그리는점들에관한정보를저장하기위한자료구조를도큐먼트클래스에추가한다. 2 도큐먼트클래스의자료구조를디스크에저장하고읽어들이기위한코드를도큐먼트클래스에추가한다. 3 사용자가점하나를추가할때마다이점을도큐먼트클래스의자료구조에도추가하기위한코드를뷰클래스에추가한다. 4 뷰의노출시내용을적절히재생하기위한코드를뷰클래스에추가한다. 이작업들을차례로살펴보자. 단계 1: 도큐먼트클래스에자료구조를추가한다
171 점을나타내는좌표들의열을저장하는방법은여러가지가있다. CPoint 배열은자연스러운선택중의하나이겠지만배열은선언할때크기가고정되는데, 이예제의경우점들의개수를미리예측하기어렵다는점에서바람직한선택은아니다. MFC에서는 CArray, CObArray, CByteArray, CUIntArray, CWordArray, CDWordArray, CPtrArray, CStringArray 등다양한배열클래스들을지원하고있는데, 이들은단순한 C++ 배열과는달리필요에따라크기가신축적으로변경될수있다. 또한이들은다양한멤버함수들을통해삽입, 추가, 삭제, 복사등의기능들을지원할뿐만아니라자체적으로 Serialize() 함수기능도지원한다. 이러한클래스들의객체에대해서는일반배열과마찬가지로 [] 안에배열인덱스를사용할수도있다. 여기서는두개의 CDWordArray를사용하여각기 x 좌표열과 y 좌표열을나타내기로한다. 이배열의원소는 32 비트정수인 DWORD 타입을갖는다. CDrawingDoc 클래스안에서 private 영역에다음과같은선언을둔다. 이는도큐먼트클래스헤더파일인 DrawingDoc.h를편집하거나도큐먼트클래스에대한팝업에서 [ 추가 - 변수추가 ] 메뉴항목을사용하여달성될수있다. CDWordArray x, y; 단계 2: 도큐먼트자료구조에대한저장및열기. AppWizard가마련한프로그램골격과 MFC 클래스들이파일의저장 / 열기등과관련한작업의많은부분을처리해주고있다. 예를들면, 그림편집기프로그램에서파일메뉴의열기항목을선택하면적절한파일열기다이얼로그를열어서읽어들일파일을선택할수있게해준다. 프로그램골격은또한선택된파일을열고그파일에연계된 CArchive 객체를만들고최종적으로 CDrawingDoc 클래스의 Serialize() 함수를호출한다. 이는파일의저장시에도마찬가지이다. 프로그래머가해야할일은 Serialize() 함수를채워도큐먼트자료구조, 즉, CDWordArray 타입의 x, y를적절히읽어들이거나저장하는일인데, CDWordArray 클래스는자체적으로 Serialize() 함수를지원하므로이작업은더욱쉬워진다. 아래코드를참고하라. void CDrawingDoc::Serialize(CArchive& ar) { x.serialize(ar); y.serialize(ar); 단계 3: 뷰클래스를수정하여추가되는점들을도큐먼트자료구조에반영한다. 앞에서뷰에그릴사각형의크기는 5x5로고정되어있었다. 그러나나중에사각형의크기를변경시키는메뉴항목을둘예정이며, 이를위해사각형의크기를나타내는 private 가시성정
172 수형멤버변수 w를뷰클래스안에둔다. ( 헝가리식표기법을따르자면 m_nwidth와같은이름을사용해야겠지만식의길이를줄이기위해 w를사용한다.) 이변수는생성자에서 5로초기화한다. 추가되는점을도큐먼트자료구조에반영하기위해 OnMouseMove() 함수는아래와같이고쳐쓴다. 추가된코드부분은진하게표시되어있다. void CDrawingView::OnMouseMove(UINT nflags, CPoint point) { CDrawingDoc* pdoc = GetDocument(); ASSERT_VALID(pDoc); if (!pdoc) return if (nflags == MK_LBUTTON) { // 마우스왼쪽버튼이눌려져있으면 CClientDC dc(this); dc.rectangle(point.x, point.y, point.x + w, point.y + w); pdoc->add(point); pdoc->setmodifiedflag(); CView::OnMouseMove(nFlags, point); 도큐먼트클래스에는 Add() 라는함수가아래와같이정의되어야한다. void CDrawingDoc::Add(CPoint point) { x.add(point.x); y.add(point.y); 단계 4: 뷰클래스에서노출이벤트의처리 가려졌던뷰가다시노출될때 WM_PAINT라는메시지가발생되며, 이는뷰클래스의 OnPaint() 함수를호출하게만드는데, 이함수는다시뷰클래스의 OnDraw() 멤버함수를호출한다. OnDraw() 함수안에노출처리코드를넣어줌으로써화면을적절히재생할수있게된다
173 void CDrawingView::OnDraw(CDC* pdc) { CDrawingDoc* pdoc = GetDocument(); ASSERT_VALID(pDoc); for (int i = 0; i < pdoc->size(); i++) { CPoint p = pdoc->getpoint(i); pdc->rectangle(p.x, p.y, p.x + w, p.y + w); 도큐먼트클래스에는 Size(), GetPoint() 등의함수들이아래와같이정의되어야한다. int CDrawingDoc::Size() { return x.getsize(); CPoint CDrawingDoc::GetPoint(int i) { return CPoint(x[i], y[i]); 지금쯤프로그램을저장한후컴파일하여실행해보고파일저장 / 열기기능과뷰노출등의기능이제대로동작하는지확인해야할좋은시점이다. 여러파일을열어두거나하나의문서에대해여러뷰를여는등의작업이제대로수행되는지도점검해보면좋을것이다. 또전체프로그램을교재를보지않고다시반복해작성해보는것도좋을것이다. 도큐먼트 / 뷰구조에관한기본적인사항은완료되었다. 하나의문서에대해여러뷰가열려있을경우실시간동기화는이루어지고있지않은데, 이는다음절에서다룰것이다. 여기에서는추가기능으로사각형의크기를변경하는메뉴항목을첨가해본다. 먼저사각형의크기를입력받을다이얼로그리소스를먼저설계한다음메뉴항목의추가및관련코드를만들어볼것이다. 단계 5: 다이얼로그리소스의추가. 프로그램에다이얼로그를추가하기위해필요한작업은다음과같다. 1 리소스편집기를사용하여새로운다이얼로그리소스를만든다. Visual Studio의 [ 서식 ] 메뉴의 [ 탭순서 ] 기능을사용하여 Edit 컨트롤이 1번이되도록한다. 2 새로운다이얼로그를위한클래스를만든다. ( 클래스이름 : CSizeDlg) 3 다이얼로그안의 Edit 컨트롤을위한 int 타입의 DDX 변수 m_nsize를정한다. 이변수
174 에대한 DDV 조건은 2에서 50 사이의값을갖는것으로정한다. 4 다이얼로그객체를생성하여화면에나타나게만든다. 이부분은다음단계의메뉴항목처리기함수안에서이루어질것이다. 단계 6: 메뉴항목및처리기함수추가. 예제의메뉴리소스를열어보면네개의메뉴가들어있으며, 주실행창에연계되어있고뷰창이하나도열려있지않을경우사용되는 IDR_MAINFRAME 메뉴와뷰창에연계된 IDR_DrawingTYPE 등의메뉴가있다. IDR_MAINFRAME 메뉴에는 [ 파일 ], [ 보기 ], [ 도움말 ] 세가지메뉴가있으며, IDR_DrawingTYPE 메뉴에는이들외에도 [ 편집 ], [ 창 ] 등의메뉴가더있다. 여기에서는 IDR_DRAWINTYPE 메뉴리소스에 [ 선택 ] 메뉴, 그리고그안에 [ 크기 ] 메뉴항목을만들어넣는다. 이를위해먼저메뉴바끝에위치한빈사각형에 [ 선택 ] 메뉴를만든후이를마우스로끌어 [ 편집 ] 메뉴와 [ 보기 ] 메뉴사이로옮긴다. 나머지는앞의그림이나타내는바와같다. 특히 [ 크기 ] 메뉴항목을위한 ID는 ID_OPTION_SIZE로정한다. 클래스마법사를사용하여추가된메뉴항목을위한처리기함수 OnOptionSize() 를 CDrawingView 클래스안에만들어넣는다. 다이얼로그는열리면서 UpdateData(FALSE) 함수를호출하여 DDX 변수들을화면에보여주고, 닫히면서 UpdateData(TRUE) 함수를호출하여사용자가입력한값을 DDX 변수들에저장한다. 따라서아래함수에서도다이얼로그가열릴때 dlg.m_isize에저장된 w의현재값이화면의 Edit 컨트롤에나타나며, 사용자가 Edit 컨트롤에입력한값은다이얼로그가닫힐때 dlg.m_nsize에저장되었다가 w로옮겨간다. #include "SizeDlg.h" void CDrawingView::OnOptionSize() { CSizeDlg dlg; dlg.m_nsize = w;
175 if (dlg.domodal() == IDOK) w = dlg.m_nsize; 이제프로그램을다시컴파일하여실행해보자. 연습문제 6. CDWordArray 클래스의멤버함수에대해조사해보라. 7. 사각형크기변경기능을사용하되, DDV 범위인 2~50 사이를벗어나는값을입력해보라. 8. 크기를변경한후뷰의일부만을다른창으로가렸다가다시나타나게해보라. 관찰된현상을설명하라. 6.4 다중뷰의동기화 그림그리기응용프로그램에서 [ 창 ] 메뉴의 [ 다음창 ] 기능을사용하여동일문서에대해두개의뷰를 [ 바둑판식 ] 으로연다음각뷰안에그림을그려넣어본다. 두개의뷰는마치별개의문서를위한것처럼보이지만주실행창을최소화시켰다가다시불러내보면각뷰에그린내용이합쳐져서나타나는것을관찰할수있을것이다. 각뷰에서추가되는점들은모두도큐먼트자료구조에추가되며, OnDraw() 함수가불려나올때는도큐먼트자료구조안에저장되어있는좌표들을사용하여새로그리게된다. 이경우각뷰에대해노출이벤트가발생하며, 따라서뷰마다 OnDraw() 함수가호출되어두개의뷰는일치된모습을보이게된다. 그러나 OnMouseMove() 함수안에서사각형을그릴때는해당뷰안에서만이루어지므로다른뷰들에대해서는실시간동기화가이루어지지않는것이다. 이문제는뷰클래스안의가상함수 OnUpdate() 함수를사용하여해결될수있다
176 CDocument 클래스객체는그문서에연계되어있는모든뷰들의목록을유지한다. 또 CDocument 클래스는 UpdateAllViews() 함수를갖고있는데, 특정문서에대해이함수가호출되면그문서에연계되어있는각뷰에대해 OnUpdate() 함수를호출한다. 이함수를적절히수정하여필요한뷰동기화를구현할수있다. 하나의사각형을추가하는작업은 OnMouseMove() 함수안에서이루어진다. 즉, 사각형추가라는사실을인지하는곳이 OnMouseMove() 함수이며, 여러뷰에동기화가필요한이벤트가발생했음을이함수에서알려줄수있다. 따라서아래와같이 OnMouseMove() 함수안에뷰동기화를위해 UpdateAllViews() 함수호출하나를추가한다. void CDrawingView::OnMouseMove(UINT nflags, CPoint point) { CDrawingDoc* pdoc = GetDocument(); ASSERT_VALID(pDoc); if (!pdoc) return; if (nflags == MK_LBUTTON) { CClientDC dc(this); dc.rectangle(point.x, point.y, point.x + w, point.y + w); pdoc->add(point); pdoc->setmodifiedflag(); pdoc->updateallviews(this, 0, 0); CView::OnMouseMove(nFlags, point); 그리고클래스마법사에서 [ 가상함수 ] 탭을사용하여가상함수 OnUpdate() 를추가한다음아래와같이편집한다. OnUpdate() 함수에서는도큐먼트자료구조의마지막좌표를받아와서사각형을그린다. void CDrawingView::OnUpdate(CView* psender, LPARAM lhint, CObject* phint) { CDrawingDoc* pdoc = GetDocument(); ASSERT_VALID(pDoc); if (!pdoc) return; int n = pdoc->size(); if (n > 0) { CPoint p = pdoc->getpoint(n - 1); CClientDC dc(this); dc.rectangle(p.x, p.y, p.x + w, p.y + w);
177 뷰의초기화과정에서불려나오는 OnInitialUpdate() 함수안에서도 OnUpdate() 함수를호출하는데, 이때는아직아무좌표도도큐먼트자료구조에들어있지않다. 이경우 OnUpdate() 함수안에서 n은 0이될것이며, 따라서이함수안의 if 문은필요함을유념하자. UpdateAllViews() 함수와 OnUpdate() 함수의파라미터는동일하며, UpdateAllViews() 함수의실인수들이 OnUpdate() 함수의파라미터로전달된다. 파라미터 lhint와 phint는뷰갱신에필요한정보의전달을위해사용될수있지만, 이예제에서는사용되고있지않으며, 이경우 UpdateAllViews() 함수에서는디폴트인수를사용할수있어생략되어도좋다. psender는문서를수정하고있는뷰를나타내며, 이뷰는이미갱신되었으므로갱신이필요없음을표시한다. 현재뷰갱신도 OnMouseMove() 함수에서처리하는대신 OnUpdate() 에맡길수있으며, 이경우 OnMouseMove() 함수는아래와같이고쳐쓸수있다. void CDrawingView::OnMouseMove(UINT nflags, CPoint point) { CDrawingDoc* pdoc = GetDocument(); ASSERT_VALID(pDoc); if (!pdoc) return; if (nflags == MK_LBUTTON) { pdoc->add(point); pdoc->setmodifiedflag(); pdoc->updateallviews(null); // 파라미터 NULL은모든뷰에 update 적용을의미 CView::OnMouseMove(nFlags, point); 이제프로그램을다시컴파일하고실행하여다중뷰에대한실시간동기화를체험해보자. 6.5 텍스트편집기의작성 또다른유형의문서를처리하는예제프로그램으로서메모장과유사한텍스트편집기응용프로그램을작성해본다. 이를위해프로젝트이름은 Ed, 응용프로그램종류는 < 다중문서 > (< 탭문서 > 선택은해제 ), 리소스언어는 < 한국어 >, 프로젝트스타일은 <MFC 표준 > 등을선택한다. 이응용프로그램의경우모든코드와리소스는 AppWizard에의해만들어지며, 프로그래머는단한줄의코드도추가하지않는다. 텍스트편집기기능을갖도록만들기위해 AppWizard 진행과정에서두가지작업을해준다
178 [ 문서템플릿속성 ] 화면에서 < 파일확장명 > 필드에확장자 tex를넣는다. ( 이과정은꼭필요한것은아니다.) [ 생성된클래스 ] 화면에서뷰클래스의 CEdView의기본클래스를 CView에서 CEditView로변경한다. 이제이프로그램을컴파일하여실행해보면텍스트의편집, 파일의저장 / 열기 / 인쇄, 텍스트의오려두기 / 복사하기 / 붙이기, 모두선택 / 찾기 / 다음찾기 / 바꾸기등의기능을사용할수있을것이다. 편집기데이터가도큐먼트클래스가아니라뷰클래스안에포함되어있음을언급하였다. 따라서도큐먼트데이터를공유하는다중뷰의사용이이경우해당되지않으며, 이는텍스트편집기를너무쉽게만든것에대한대가로생각해야할것이다. 따라서 [ 창 ] 메뉴의 [ 새창 ] 기능은불필요하므로삭제하는것이바람직할것이다. 오른쪽의 [ 문서템플릿속성 ] 화면에서정해지는스트링들은스트링리소스의 IDR_MAINFRAME 스트링과 IDR_EDTYPE 스트링에아래와같이들어있다. 모두 7 개의스트링인이들에대한설명은 CDocTemplate::GetDocString() 함수에대한도움말안에포함되어있다
179 1 CDocTemplate::windowTitle (IDR_MAINFRAME: "Ed") 응용프로그램의주실행창타이틀바에나타나는이름. ( 예 : "Microsoft Excel") 2 CDocTemplate::docName ("Ed") 문서의디폴트이름어간부분. ( 예 : "Book"). 파일메뉴의새파일기능을선택할때디폴트파일이름은여기에숫자를붙여만들어진다. ( 예 : "Book1", "Book2" 등등 ). 지정되어있지않으면 "Untitled" 또는 " 제목없음 " 등이사용된다. 3 CDocTemplate::fileNewName ("Ed") 문서유형을나타내는이름. 응용프로그램이둘이상의문서유형을지원할때새파일메뉴항목을선택하면파일유형목록이나타나는데, 여기에사용되는스트링이다.( 예 : "Worksheet"). 4 CDocTemplate::filterExt (".tex") 문서 유형을 위한 파일 확장자. ( 예 : ".xls"). 5 CDocTemplate::filterName ("Ed Files (*.tex)") 문서유형에대한설명과와일드카드필터. 파일열기다이얼로그의파일형식필드에사용되는스트링이다. ( 예 : "Worksheets (*.xls)") 6 CDocTemplate::regFileTypeId ("Ed.Document") 윈도우운영체제에등록되어있는파일유형식별자. ( 예 : "ExcelWorksheet") 7 CDocTemplate::regFileTypeName ("Ed.Document") 윈도우운영체제에등록되어있는파일유형이름. ( 예 : "Microsoft Excel Worksheet")
Programming hwp
struct MerchandiseTable { CatalogEntry tab[10]; void init(); char const *getname(char const *code); int getprice(char const *code); void MerchandiseTable::init() { strcpy(tab[0].code, "m01"); strcpy(tab[0].name,
More informationProgramming hwp
// 현재노드다음에포인터 p 가가리키는노드를삽입한다. void Dlink::insert(Dlink* p) { p->succ = succ; // (1) p->pred = this; // (2) succ->pred = p; // (3) succ = p; // (4) 2.7 상속 (inheritance) 새로운클래스 B를정의해야하는데기존의클래스중유사한기능의클래스
More informationMicrosoft PowerPoint - chap02-C프로그램시작하기.pptx
#include int main(void) { int num; printf( Please enter an integer "); scanf("%d", &num); if ( num < 0 ) printf("is negative.\n"); printf("num = %d\n", num); return 0; } 1 학습목표 을 작성하면서 C 프로그램의
More information1. auto_ptr 다음프로그램의문제점은무엇인가? void func(void) int *p = new int; cout << " 양수입력 : "; cin >> *p; if (*p <= 0) cout << " 양수를입력해야합니다 " << endl; return; 동적할
15 장기타주제들 auto_ptr 변환함수 cast 연산자에의한명시적형변환실행시간타입정보알아내기 (RTTI) C++ 프로그래밍입문 1. auto_ptr 다음프로그램의문제점은무엇인가? void func(void) int *p = new int; cout > *p; if (*p
More information설계란 무엇인가?
금오공과대학교 C++ 프로그래밍 jhhwang@kumoh.ac.kr 컴퓨터공학과 황준하 9 강. 클래스의활용목차 멤버함수의외부정의 this 포인터 friend 선언 static 멤버 임시객체 1 /17 9 강. 클래스의활용멤버함수의외부정의 멤버함수정의구현방법 내부정의 : 클래스선언내에함수정의구현 외부정의 클래스선언 : 함수프로토타입 멤버함수정의 : 클래스선언외부에구현
More information<322EBCF8C8AF28BFACBDC0B9AEC1A6292E687770>
연습문제해답 5 4 3 2 1 0 함수의반환값 =15 5 4 3 2 1 0 함수의반환값 =95 10 7 4 1-2 함수의반환값 =3 1 2 3 4 5 연습문제해답 1. C 언어에서의배열에대하여다음중맞는것은? (1) 3차원이상의배열은불가능하다. (2) 배열의이름은포인터와같은역할을한다. (3) 배열의인덱스는 1에서부터시작한다. (4) 선언한다음, 실행도중에배열의크기를변경하는것이가능하다.
More informationMicrosoft PowerPoint - C++ 5 .pptx
C++ 언어프로그래밍 한밭대학교전자. 제어공학과이승호교수 연산자중복 (operator overloading) 이란? 2 1. 연산자중복이란? 1) 기존에미리정의되어있는연산자 (+, -, /, * 등 ) 들을프로그래머의의도에맞도록새롭게정의하여사용할수있도록지원하는기능 2) 연산자를특정한기능을수행하도록재정의하여사용하면여러가지이점을가질수있음 3) 하나의기능이프로그래머의의도에따라바뀌어동작하는다형성
More information쉽게 풀어쓴 C 프로그래밍
제 3 장함수와문자열 1. 함수의기본적인개념을이해한다. 2. 인수와매개변수의개념을이해한다. 3. 함수의인수전달방법 2가지를이해한다 4. 중복함수를이해한다. 5. 디폴트매개변수를이해한다. 6. 문자열의구성을이해한다. 7. string 클래스의사용법을익힌다. 이번장에서만들어볼프로그램 함수란? 함수선언 함수호출 예제 #include using
More informationMicrosoft PowerPoint - additional01.ppt [호환 모드]
1.C 기반의 C++ part 1 함수 오버로딩 (overloading) 디폴트매개변수 (default parameter) 인-라인함수 (in-line function) 이름공간 (namespace) Jong Hyuk Park 함수 Jong Hyuk Park 함수오버로딩 (overloading) 함수오버로딩 (function overloading) C++ 언어에서는같은이름을가진여러개의함수를정의가능
More informationMicrosoft PowerPoint - [2009] 02.pptx
원시데이터유형과연산 원시데이터유형과연산 원시데이터유형과연산 숫자데이터유형 - 숫자데이터유형 원시데이터유형과연산 표준입출력함수 - printf 문 가장기본적인출력함수. (stdio.h) 문법 ) printf( Test printf. a = %d \n, a); printf( %d, %f, %c \n, a, b, c); #include #include
More informationq 이장에서다룰내용 1 객체지향프로그래밍의이해 2 객체지향언어 : 자바 2
객체지향프로그래밍 IT CookBook, 자바로배우는쉬운자료구조 q 이장에서다룰내용 1 객체지향프로그래밍의이해 2 객체지향언어 : 자바 2 q 객체지향프로그래밍의이해 v 프로그래밍기법의발달 A 군의사업발전 1 단계 구조적프로그래밍방식 3 q 객체지향프로그래밍의이해 A 군의사업발전 2 단계 객체지향프로그래밍방식 4 q 객체지향프로그래밍의이해 v 객체란무엇인가
More informationMicrosoft PowerPoint - ch07 - 포인터 pm0415
2015-1 프로그래밍언어 7. 포인터 (Pointer), 동적메모리할당 2015 년 4 월 4 일 교수김영탁 영남대학교공과대학정보통신공학과 (Tel : +82-53-810-2497; Fax : +82-53-810-4742 http://antl.yu.ac.kr/; E-mail : ytkim@yu.ac.kr) Outline 포인터 (pointer) 란? 간접참조연산자
More informationC++ Programming
C++ Programming 연산자다중정의 Seo, Doo-okok clickseo@gmail.com http://www.clickseo.com 목 차 연산자다중정의 C++ 스타일의문자열 2 연산자다중정의 연산자다중정의 단항연산자다중정의 이항연산자다중정의 cin, cout 그리고 endl C++ 스타일의문자열 3 연산자다중정의 연산자다중정의 (Operator
More informationPowerPoint Template
16-1. 보조자료템플릿 (Template) 함수템플릿 클래스템플릿 Jong Hyuk Park 함수템플릿 Jong Hyuk Park 함수템플릿소개 함수템플릿 한번의함수정의로서로다른자료형에대해적용하는함수 예 int abs(int n) return n < 0? -n : n; double abs(double n) 함수 return n < 0? -n : n; //
More information쉽게 풀어쓴 C 프로그래밍
제 5 장생성자와접근제어 1. 객체지향기법을이해한다. 2. 클래스를작성할수있다. 3. 클래스에서객체를생성할수있다. 4. 생성자를이용하여객체를초기화할수 있다. 5. 접근자와설정자를사용할수있다. 이번장에서만들어볼프로그램 생성자 생성자 (constructor) 는초기화를담당하는함수 생성자가필요한이유 #include using namespace
More informationMicrosoft PowerPoint - 3ÀÏ°_º¯¼ö¿Í »ó¼ö.ppt
변수와상수 1 변수란무엇인가? 변수 : 정보 (data) 를저장하는컴퓨터내의특정위치 ( 임시저장공간 ) 메모리, register 메모리주소 101 번지 102 번지 변수의크기에따라 주로 byte 단위 메모리 2 기본적인변수형및변수의크기 변수의크기 해당컴퓨터에서는항상일정 컴퓨터마다다를수있음 short
More information슬라이드 1
-Part3- 제 4 장동적메모리할당과가변인 자 학습목차 4.1 동적메모리할당 4.1 동적메모리할당 4.1 동적메모리할당 배울내용 1 프로세스의메모리공간 2 동적메모리할당의필요성 4.1 동적메모리할당 (1/6) 프로세스의메모리구조 코드영역 : 프로그램실행코드, 함수들이저장되는영역 스택영역 : 매개변수, 지역변수, 중괄호 ( 블록 ) 내부에정의된변수들이저장되는영역
More informationProgramming hwp
3 장 MFC 프로그래밍 윈도우응용프로그램개발을용이하게할수있도록 Visual C++ 에서는 MFC 클래스라이브러리와응용프로그램마법사 (AppWizard), 클래스마법사 (ClassWizard), 리소스편집기 (Resource Editor) 등의다양한도구를제공하고있다. 이장에서는응용프로그램마법사등의도구들은사용하지않고 MFC만을사용하여윈도우응용프로그램을작성하는방법을설명한다.
More informationMicrosoft PowerPoint - Chapter 6.ppt
6.Static 멤버와 const 멤버 클래스와 const 클래스와 static 연결리스트프로그램예 Jong Hyuk Park 클래스와 const Jong Hyuk Park C 의 const (1) const double PI=3.14; PI=3.1415; // 컴파일오류 const int val; val=20; // 컴파일오류 3 C 의 const (1)
More informationMicrosoft PowerPoint - ch09 - 연결형리스트, Stack, Queue와 응용 pm0100
2015-1 프로그래밍언어 9. 연결형리스트, Stack, Queue 2015 년 5 월 4 일 교수김영탁 영남대학교공과대학정보통신공학과 (Tel : +82-53-810-2497; Fax : +82-53-810-4742 http://antl.yu.ac.kr/; E-mail : ytkim@yu.ac.kr) 연결리스트 (Linked List) 연결리스트연산 Stack
More informationchap10.PDF
10 C++ Hello!! C C C++ C++ C++ 2 C++ 1980 Bell Bjarne Stroustrup C++ C C++ C, C++ C C 3 C C++ (prototype) (type checking) C C++ : C++ 4 C C++ (prototype) (type checking) [ 10-1] #include extern
More information슬라이드 1
정적메모리할당 (Static memory allocation) 일반적으로프로그램의실행에필요한메모리 ( 변수, 배열, 객체등 ) 는컴파일과정에서결정되고, 실행파일이메모리에로드될때할당되며, 종료후에반환됨 동적메모리할당 (Dynamic memory allocation) 프로그램의실행중에필요한메모리를할당받아사용하고, 사용이끝나면반환함 - 메모리를프로그램이직접관리해야함
More information(Microsoft Word - \301\337\260\243\260\355\273\347.docx)
내장형시스템공학 (NH466) 중간고사 학번 : 이름 : 문제 배점 점수 1 20 2 20 3 20 4 20 5 10 6 10 7 15 8 20 9 15 합계 150 1. (20 점 ) 다음용어에대해서설명하시오. (1) 정보은닉 (Information Hiding) (2) 캡슐화 (Encapsulation) (3) 오버로딩 (Overloading) (4) 생성자
More information<443A5C4C C4B48555C B3E25C32C7D0B1E25CBCB3B0E8C7C1B7CEC1A7C6AE425CC0E7B0EDB0FCB8AE5C53746F636B5F4D616E D656E74732E637070>
1 #include 2 #include 3 #include 4 #include 5 #include 6 #include "QuickSort.h" 7 using namespace std; 8 9 10 Node* Queue[100]; // 추가입력된데이터를저장하기위한 Queue
More information(Microsoft PowerPoint - 11\300\345.ppt [\310\243\310\257 \270\360\265\345])
입출력 C++ 의효율적인입출력방법을배워보자. 이장에서다룰내용 1 cin 과 cout 을이용한입출력 2 입출력연산자중복 3 조작자생성 4 파일입출력 01_cin 과 cout 을이용한입출력 포맷입출력 C++ 의표준입출력은 cin, cout 을사용한다. C 의 printf 는함수이므로매번여러인자를입력해줘야하지만, cin/cout 에서는형식을한번만정의하면계속사용할수있다.
More information금오공대 컴퓨터공학전공 강의자료
C 프로그래밍프로젝트 Chap 14. 포인터와함수에대한이해 2013.10.09. 오병우 컴퓨터공학과 14-1 함수의인자로배열전달 기본적인인자의전달방식 값의복사에의한전달 val 10 a 10 11 Department of Computer Engineering 2 14-1 함수의인자로배열전달 배열의함수인자전달방식 배열이름 ( 배열주소, 포인터 ) 에의한전달 #include
More informationA Hierarchical Approach to Interactive Motion Editing for Human-like Figures
단일연결리스트 (Singly Linked List) 신찬수 연결리스트 (linked list)? tail 서울부산수원용인 null item next 구조체복습 struct name_card { char name[20]; int date; } struct name_card a; // 구조체변수 a 선언 a.name 또는 a.date // 구조체 a의멤버접근 struct
More informationSlide 1
SeoulTech 2011-2 nd 프로그래밍입문 (2) Chapter 14. 상속 박종혁교수 (http://www.parkjonghyuk.net) Tel: 970-6702 Email: jhpark1@snut.ac.kr Learning Objectives 상속의기본 파생클래스와생성자 protected: 제한자 멤버함수의재정의 상속되지않는함수들 상속을이용한프로그래밍
More informationchap 5: Trees
5. Threaded Binary Tree 기본개념 n 개의노드를갖는이진트리에는 2n 개의링크가존재 2n 개의링크중에 n + 1 개의링크값은 null Null 링크를다른노드에대한포인터로대체 Threads Thread 의이용 ptr left_child = NULL 일경우, ptr left_child 를 ptr 의 inorder predecessor 를가리키도록변경
More informationC# Programming Guide - Types
C# Programming Guide - Types 최도경 lifeisforu@wemade.com 이문서는 MSDN 의 Types 를요약하고보충한것입니다. http://msdn.microsoft.com/enus/library/ms173104(v=vs.100).aspx Types, Variables, and Values C# 은 type 에민감한언어이다. 모든
More informationK&R2 Reference Manual 번역본
typewriter structunion struct union if-else if if else if if else if if if if else else ; auto register static extern typedef void char short int long float double signed unsigned const volatile { } struct
More informationgnu-lee-oop-kor-lec06-3-chap7
어서와 Java 는처음이지! 제 7 장상속 Super 키워드 상속과생성자 상속과다형성 서브클래스의객체가생성될때, 서브클래스의생성자만호출될까? 아니면수퍼클래스의생성자도호출되는가? class Base{ public Base(String msg) { System.out.println("Base() 생성자 "); ; class Derived extends Base
More informationPowerPoint Presentation
Class - Property Jo, Heeseung 목차 section 1 클래스의일반구조 section 2 클래스선언 section 3 객체의생성 section 4 멤버변수 4-1 객체변수 4-2 클래스변수 4-3 종단 (final) 변수 4-4 멤버변수접근방법 section 5 멤버변수접근한정자 5-1 public 5-2 private 5-3 한정자없음
More information11장 포인터
누구나즐기는 C 언어콘서트 제 9 장포인터 이번장에서학습할내용 포인터이란? 변수의주소 포인터의선언 간접참조연산자 포인터연산 포인터와배열 포인터와함수 이번장에서는포인터의기초적인지식을학습한다. 포인터란? 포인터 (pointer): 주소를가지고있는변수 메모리의구조 변수는메모리에저장된다. 메모리는바이트단위로액세스된다. 첫번째바이트의주소는 0, 두번째바이트는 1, 변수와메모리
More information설계란 무엇인가?
금오공과대학교 C++ 프로그래밍 jhhwang@kumoh.ac.kr 컴퓨터공학과 황준하 16 강. 파일입출력목차 파일입출력기초 파일입출력모드 텍스트파일과이진파일 이진파일입출력 임의접근 1 /18 16 강. 파일입출력파일입출력기초 파일입출력과정 파일스트림객체생성 파일열기 사용 : 기본적으로표준입출력객체 (cin, cout) 사용방법과동일 파일닫기 파일스트림클래스의종류
More information쉽게 풀어쓴 C 프로그래밍
제 13 장파일처리 1. 스트림의개념을이해한다. 2. 객체지향적인방법을사용하여파일입출력을할수있다. 3. 텍스트파일과이진파일의차이점을이해한다. 4. 순차파일과임의접근파일의차이점을이해한다. 이번장에서만들어볼프로그램 스트림 (stream) 스트림 (stream) 은 순서가있는데이터의연속적인흐름 이다. 스트림은입출력을물의흐름처럼간주하는것이다. 입출력관련클래스들 파일쓰기
More informationC++ Programming
C++ Programming 예외처리 Seo, Doo-okok clickseo@gmail.com http://www.clickseo.com 목 차 예외처리 2 예외처리 예외처리 C++ 의예외처리 예외클래스와객체 3 예외처리 예외를처리하지않는프로그램 int main() int a, b; cout > a >> b; cout
More informationC++ Programming
C++ Programming 상속과다형성 Seo, Doo-okok clickseo@gmail.com http://www.clickseo.com 목 차 상속의이해 상속과다형성 다중상속 2 상속과다형성 객체의이해 상속클래스의객체의생성및소멸 상속의조건 상속과다형성 다중상속 3 상속의이해 상속 (Inheritance) 클래스에구현된모든특성 ( 멤버변수와멤버함수 )
More information4 장클래스와객체 클래스와객체 public과 private 구조체와클래스객체의생성과생성자객체의소멸과소멸자생성자와소멸자의호출순서디폴트생성자와디폴트소멸자멤버초기화멤버함수의외부정의멤버함수의인라인함수선언 C++ 프로그래밍입문
4 장클래스와객체 클래스와객체 public과 private 구조체와클래스객체의생성과생성자객체의소멸과소멸자생성자와소멸자의호출순서디폴트생성자와디폴트소멸자멤버초기화멤버함수의외부정의멤버함수의인라인함수선언 C++ 프로그래밍입문 1. 클래스와객체 추상데이터형 : 속성 (attribute) + 메서드 (method) 예 : 자동차의속성과메서드 C++ : 주로 class
More information1. 객체의생성과대입 int 형변수 : 선언과동시에초기화하는방법 (C++) int a = 3; int a(3); // 기본타입역시클래스와같이처리가능 객체의생성 ( 복습 ) class CPoint private : int x, y; public : CPoint(int a
6 장복사생성자 객체의생성과대입객체의값에의한전달복사생성자디폴트복사생성자복사생성자의재정의객체의값에의한반환임시객체 C++ 프로그래밍입문 1. 객체의생성과대입 int 형변수 : 선언과동시에초기화하는방법 (C++) int a = 3; int a(3); // 기본타입역시클래스와같이처리가능 객체의생성 ( 복습 ) class CPoint private : int x, y;
More informationC++ Programming
C++ Programming 클래스와데이터추상화 Seo, Doo-okok clickseo@gmail.com http://www.clickseo.com 목 차 객체지향프로그래밍 클래스와객체 2 객체지향프로그래밍 객체지향언어 (Object-Oriented Language) 프로그램을명령어의목록으로보는시각에서벗어나여러개의 독립된단위, 즉 객체 (Object) 들의모임으로파악
More information쉽게 풀어쓴 C 프로그래밍
Power Java 제 7 장클래스와객체 이번장에서학습할내용 객체지향이란? 객체 메시지 클래스 객체지향의장점 String 클래스 객체지향개념을완벽하게이해해야만객체지향설계의이점을활용할수있다. 실제세계는객체로이루어진다. 객체지향이란? 실제세계를모델링하여소프트웨어를개발하는방법 절차지향과객체지향 절차지향프로그래밍 (procedural programming): 문제를해결하는절차를중요하게생각하는방법
More informationPowerPoint 프레젠테이션
System Software Experiment 1 Lecture 5 - Array Spring 2019 Hwansoo Han (hhan@skku.edu) Advanced Research on Compilers and Systems, ARCS LAB Sungkyunkwan University http://arcs.skku.edu/ 1 배열 (Array) 동일한타입의데이터가여러개저장되어있는저장장소
More information(Microsoft PowerPoint - 07\300\345.ppt [\310\243\310\257 \270\360\265\345])
클래스의응용 클래스를자유자재로사용하자. 이장에서다룰내용 1 객체의치환 2 함수와클래스의상관관계 01_ 객체의치환 객체도변수와마찬가지로치환이가능하다. 기본예제 [7-1] 객체도일반변수와마찬가지로대입이가능하다. 기본예제 [7-2] 객체의치환시에는조심해야할점이있다. 복사생성자의필요성에대하여알아보자. [ 기본예제 7-1] 클래스의치환 01 #include
More information쉽게 풀어쓴 C 프로그래밍
제 11 장상속 1. 상속의개념을이해한다. 2. 상속을이용하여자식클래스를작성할수있다. 3. 상속과접근지정자와의관계를이해한다. 4. 상속시생성자와소멸자가호출되는순서를이해한다. 이번장에서만들어볼프로그램 class Circle { int x, y; int radius;... class Rect { int x, y; int width, height;... 중복 상속의개요
More informationJAVA 프로그래밍실습 실습 1) 실습목표 - 메소드개념이해하기 - 매개변수이해하기 - 새메소드만들기 - Math 클래스의기존메소드이용하기 ( ) 문제 - 직사각형모양의땅이있다. 이땅의둘레, 면적과대각
JAVA 프로그래밍실습 실습 1) 실습목표 - 메소드개념이해하기 - 매개변수이해하기 - 새메소드만들기 - Math 클래스의기존메소드이용하기 ( http://java.sun.com/javase/6/docs/api ) 문제 - 직사각형모양의땅이있다. 이땅의둘레, 면적과대각선의길이를계산하는메소드들을작성하라. 직사각형의가로와세로의길이는주어진다. 대각선의길이는 Math클래스의적절한메소드를이용하여구하라.
More informationMicrosoft PowerPoint - additional06.ppt [호환 모드]
보조자료 6.Static 멤버와 const 멤버 클래스와 const 클래스와 static 연결리스트프로그램예 Jong Hyuk Park 클래스와 const Jong Hyuk Park 복습 : Const 키워드왜사용? C 의 const (1) const double PI=3.14; PI=3.1415; // 컴파일오류 const int val; val=20; //
More informationMicrosoft PowerPoint - additional08.ppt [호환 모드]
8. 상속과다형성 (polymorphism) 상속된객체와포인터 / 참조자의관계 정적바인딩과동적바인딩 virtual 소멸자 Jong Hyuk Park 상속의조건 public 상속은 is-a 관계가성립되도록하자. 일반화 ParttimeStd 구체화 2 상속의조건 잘못된상속의예 현실세계와완전히동떨어진모델이형성됨 3 상속의조건 HAS-A( 소유 ) 관계에의한상속!
More information[ 마이크로프로세서 1] 2 주차 3 차시. 포인터와구조체 2 주차 3 차시포인터와구조체 학습목표 1. C 언어에서가장어려운포인터와구조체를설명할수있다. 2. Call By Value 와 Call By Reference 를구분할수있다. 학습내용 1 : 함수 (Functi
2 주차 3 차시포인터와구조체 학습목표 1. C 언어에서가장어려운포인터와구조체를설명할수있다. 2. Call By Value 와 Call By Reference 를구분할수있다. 학습내용 1 : 함수 (Function) 1. 함수의개념 입력에대해적절한출력을발생시켜주는것 내가 ( 프로그래머 ) 작성한명령문을연산, 처리, 실행해주는부분 ( 모듈 ) 자체적으로실행되지않으며,
More information<4D F736F F F696E74202D2036C0CFC2B05FB0B4C3BCC1F6C7E2C7C1B7CEB1D7B7A1B9D62E707074>
객체지향프로그램밍 (Object-Oriented Programming) 1 C++ popular C 객체지향 (object oriented) C++ C : 상위계층언어특징 + 어셈블리언어특징 C++ : 소프트웨어개발플랫폼에객체지향개념제공 객체지향 : 자료와이들자료를어떻게다룰것인지따로생각하지않고단지하나의사물로생각 형 변수가사용하는메모리크기 변수가가질수있는정보
More information설계란 무엇인가?
금오공과대학교 C++ 프로그래밍 jhhwang@kumoh.ac.kr 컴퓨터공학과 황준하 5 강. 배열, 포인터, 참조목차 배열 포인터 C++ 메모리구조 주소연산자 포인터 포인터연산 배열과포인터 메모리동적할당 문자열 참조 1 /20 5 강. 배열, 포인터, 참조배열 배열 같은타입의변수여러개를하나의변수명으로처리 int Ary[10]; 총 10 개의변수 : Ary[0]~Ary[9]
More informationMicrosoft PowerPoint - chap10-함수의활용.pptx
#include int main(void) { int num; printf( Please enter an integer: "); scanf("%d", &num); if ( num < 0 ) printf("is negative.\n"); printf("num = %d\n", num); return 0; } 1 학습목표 중 값에 의한 전달 방법과
More informationMicrosoft PowerPoint - chap06-2pointer.ppt
2010-1 학기프로그래밍입문 (1) chapter 06-2 참고자료 포인터 박종혁 Tel: 970-6702 Email: jhpark1@snut.ac.kr 한빛미디어 출처 : 뇌를자극하는 C프로그래밍, 한빛미디어 -1- 포인터의정의와사용 변수를선언하는것은메모리에기억공간을할당하는것이며할당된이후에는변수명으로그기억공간을사용한다. 할당된기억공간을사용하는방법에는변수명외에메모리의실제주소값을사용하는것이다.
More informationJAVA PROGRAMMING 실습 08.다형성
2015 학년도 2 학기 1. 추상메소드 선언은되어있으나코드구현되어있지않은메소드 abstract 키워드사용 메소드타입, 이름, 매개변수리스트만선언 public abstract String getname(); public abstract void setname(string s); 2. 추상클래스 abstract 키워드로선언한클래스 종류 추상메소드를포함하는클래스
More information설계란 무엇인가?
금오공과대학교 C++ 프로그래밍 jhhwang@kumoh.ac.kr 컴퓨터공학과 황준하 6 강. 함수와배열, 포인터, 참조목차 함수와포인터 주소값의매개변수전달 주소의반환 함수와배열 배열의매개변수전달 함수와참조 참조에의한매개변수전달 참조의반환 프로그래밍연습 1 /15 6 강. 함수와배열, 포인터, 참조함수와포인터 C++ 매개변수전달방법 값에의한전달 : 변수값,
More informationA Dynamic Grid Services Deployment Mechanism for On-Demand Resource Provisioning
C Programming Practice (II) Contents 배열 문자와문자열 구조체 포인터와메모리관리 구조체 2/17 배열 (Array) (1/2) 배열 동일한자료형을가지고있으며같은이름으로참조되는변수들의집합 배열의크기는반드시상수이어야한다. type var_name[size]; 예 ) int myarray[5] 배열의원소는원소의번호를 0 부터시작하는색인을사용
More information설계란 무엇인가?
금오공과대학교 C++ 프로그래밍 jhhwang@kumoh.ac.kr 컴퓨터공학과 황준하 15 강. 표준입출력목차 C++ 입출력클래스 입출력형식설정방법 setf, unsetf 멤버함수에의한입출력형식설정 setf 이외의멤버함에의한입출력형식설정 입출력조작자에의한입출력형식설정 문자및문자열입출력멤버함수 문자단위입출력 줄단위입력 입출력스트림상태 string 클래스 complex
More information11장 포인터
Dynamic Memory and Linked List 1 동적할당메모리의개념 프로그램이메모리를할당받는방법 정적 (static) 동적 (dynamic) 정적메모리할당 프로그램이시작되기전에미리정해진크기의메모리를할당받는것 메모리의크기는프로그램이시작하기전에결정 int i, j; int buffer[80]; char name[] = data structure"; 처음에결정된크기보다더큰입력이들어온다면처리하지못함
More informationPowerPoint Template
JavaScript 회원정보 입력양식만들기 HTML & JavaScript Contents 1. Form 객체 2. 일반적인입력양식 3. 선택입력양식 4. 회원정보입력양식만들기 2 Form 객체 Form 객체 입력양식의틀이되는 태그에접근할수있도록지원 Document 객체의하위에위치 속성들은모두 태그의속성들의정보에관련된것
More informationiii. Design Tab 을 Click 하여 WindowBuilder 가자동으로생성한 GUI 프로그래밍환경을확인한다.
Eclipse 개발환경에서 WindowBuilder 를이용한 Java 프로그램개발 이예는 Java 프로그램의기초를이해하고있는사람을대상으로 Embedded Microcomputer 를이용한제어시스템을 PC 에서 Serial 통신으로제어 (Graphical User Interface (GUI) 환경에서 ) 하는프로그램개발예를설명한다. WindowBuilder:
More information1. 클래스와배열 int 형배열선언및초기화 int ary[5] = 1, 2, 3, 4, 5 ; for (int i = 0; i < 5; i++) cout << "ary[" << i << "] = " << ary[i] << endl; 5 장클래스의활용 1
5 장클래스의활용 클래스와배열객체포인터 this 포인터멤버함수오버로딩디폴트매개변수의사용 friend ( 전역함수, 클래스, 멤버함수 ) 내포클래스지역클래스 static 멤버 const 멤버와 const 객체 explicit 생성자 C++ 프로그래밍입문 1. 클래스와배열 int 형배열선언및초기화 int ary[5] = 1, 2, 3, 4, 5 ; for (int
More informationMicrosoft PowerPoint - Java7.pptx
HPC & OT Lab. 1 HPC & OT Lab. 2 실습 7 주차 Jin-Ho, Jang M.S. Hanyang Univ. HPC&OT Lab. jinhoyo@nate.com HPC & OT Lab. 3 Component Structure 객체 (object) 생성개념을이해한다. 외부클래스에대한접근방법을이해한다. 접근제어자 (public & private)
More informationPowerPoint Presentation
public class SumTest { public static void main(string a1[]) { int a, b, sum; a = Integer.parseInt(a1[0]); b = Integer.parseInt(a1[1]); sum = a + b ; // 두수를더하는부분입니다 System.out.println(" 두수의합은 " + sum +
More informationMicrosoft PowerPoint - CSharp-10-예외처리
10 장. 예외처리 예외처리개념 예외처리구문 사용자정의예외클래스와예외전파 순천향대학교컴퓨터학부이상정 1 예외처리개념 순천향대학교컴퓨터학부이상정 2 예외처리 오류 컴파일타임오류 (Compile-Time Error) 구문오류이기때문에컴파일러의구문오류메시지에의해쉽게교정 런타임오류 (Run-Time Error) 디버깅의절차를거치지않으면잡기어려운심각한오류 시스템에심각한문제를줄수도있다.
More information17장 클래스와 메소드
17 장클래스와메소드 박창이 서울시립대학교통계학과 박창이 ( 서울시립대학교통계학과 ) 17 장클래스와메소드 1 / 18 학습내용 객체지향특징들객체출력 init 메소드 str 메소드연산자재정의타입기반의버전다형성 (polymorphism) 박창이 ( 서울시립대학교통계학과 ) 17 장클래스와메소드 2 / 18 객체지향특징들 객체지향프로그래밍의특징 프로그램은객체와함수정의로구성되며대부분의계산은객체에대한연산으로표현됨객체의정의는
More informationMicrosoft Word - FunctionCall
Function all Mechanism /* Simple Program */ #define get_int() IN KEYOARD #define put_int(val) LD A val \ OUT MONITOR int add_two(int a, int b) { int tmp; tmp = a+b; return tmp; } local auto variable stack
More informationMicrosoft PowerPoint - Chap12-OOP.ppt
객체지향프로그래밍 (Object Oriented Programming) 12 장강사 강대기 차례 (Agenda) 멤버에대한동적메모리할당 암시적 / 명시적복사생성자 암시적 / 명시적오버로딩대입연산자 생성자에 new 사용하기 static 클래스멤버 객체에위치지정 new 사용하기 객체를지시하는포인터 StringBad 클래스 멤버에포인터사용 str static 멤버
More informationMicrosoft PowerPoint - Chapter 10.ppt
10. 연산자오버로딩 연산자오버로딩소개 이항연산자오버로딩 단항연산자의오버로딩 cout, cin, endl 구현 배열인덱스연산자오버로딩 대입연산자오버로딩 Jong Hyuk Park 연산자오버로딩소개 Jong Hyuk Park 연산자오버로딩 (operator overloading) C++ 에서는기존의 C 언어에서제공하고있는연산자에대하여그의미를다시부여하는것을 "
More informationMicrosoft PowerPoint - 8ÀÏ°_Æ÷ÀÎÅÍ.ppt
포인터 1 포인터란? 포인터 메모리의주소를가지고있는변수 메모리주소 100번지 101번지 102번지 103번지 int theage (4 byte) 변수의크기에따라 주로 byte 단위 주소연산자 : & 변수의주소를반환 메모리 2 #include list 8.1 int main() using namespace std; unsigned short
More informationMicrosoft PowerPoint - additional07.ppt [호환 모드]
보충자료 7. 상속 (inheritance) 의이해 상속의기본개념 상속의생성자, 소멸자 protected 멤버 Jong Hyuk Park 상속의기본개념 Jong Hyuk Park 상속의기본개념 상속의예 1 " 철수는아버지로부터좋은목소리와큰키를물려 받았다." 상속의예 2 "Student 클래스가 Person 클래스를상속한다." 아버지 Person 철수 Stduent
More informationChapter 4. LISTS
6. 동치관계 (Equivalence Relations) 동치관계 reflexive, symmetric, transitive 성질을만족 "equal to"(=) 관계는동치관계임. x = x x = y 이면 y = x x = y 이고 y = z 이면 x = z 동치관계를이용하여집합 S 를 동치클래스 로분할 동일한클래스내의원소 x, y 에대해서는 x y 관계성립
More informationProgramming hwp
4 장대화상자기반응용프로그램 3장에서는윈도우응용프로그램작성을위한 MFC 프로그래밍에텍스트편집기만을사용하였으나, 이장에서부터는응용프로그램마법사 (AppWizard), 리소스편집기, 클래스마법사 (Class Wizard) 등의도구를활용한다. 이러한도구들의이용으로전체프로그램소스에서프로그래머가직접코딩하는분량을크게줄일수있어빠른시간안에응용프로그램의개발이가능해진다. 또한
More informationSlide 1
SeoulTech 2011-2 nd 프로그래밍입문 (2) Chapter 6. 구조체와클래스 박종혁교수 (http://www.parkjonghyuk.net) Tel: 970-6702 Email: jhpark1@snut.ac.kr Learning Objectives 구조체 구조체형 함수매개변수로서의구조체 구조체초기화 클래스 정의, 멤버함수 public 과 private
More informationuntitled
시스템소프트웨어 : 운영체제, 컴파일러, 어셈블러, 링커, 로더, 프로그래밍도구등 소프트웨어 응용소프트웨어 : 워드프로세서, 스프레드쉬트, 그래픽프로그램, 미디어재생기등 1 n ( x + x +... + ) 1 2 x n 00001111 10111111 01000101 11111000 00001111 10111111 01001101 11111000
More information설계란 무엇인가?
금오공과대학교 C++ 프로그래밍 jhhwang@kumoh.ac.kr 컴퓨터공학과 황준하 4 강. 함수와라이브러리함수목차 함수오버로딩 디폴트매개변수 라이브러리함수 clock 함수 난수발생 비버퍼형문자입력 커서이동 프로그래밍문제 1 /21 4 강. 함수와라이브러리함수함수오버로딩 2 /21 함수오버로딩 동일한이름의함수를여러개만들수있음 함수프로파일이달라야함 함수프로파일
More information<4D F736F F F696E74202D20B8AEB4AABDBA20BFC0B7F920C3B3B8AEC7CFB1E22E BC8A3C8AF20B8F0B5E55D>
리눅스 오류처리하기 2007. 11. 28 안효창 라이브러리함수의오류번호얻기 errno 변수기능오류번호를저장한다. 기본형 extern int errno; 헤더파일 라이브러리함수호출에실패했을때함수예 정수값을반환하는함수 -1 반환 open 함수 포인터를반환하는함수 NULL 반환 fopen 함수 2 유닉스 / 리눅스 라이브러리함수의오류번호얻기 19-1
More informationAPI - Notification 메크로를통하여어느특정상황이되었을때 SolidWorks 및보낸경로를통하여알림메시지를보낼수있습니다. 이번기술자료에서는메크로에서이벤트처리기를통하여진행할예정이며, 메크로에서작업을수행하는데유용할것입니다. 알림이벤트핸들러는응용프로그램구현하는데있어
메크로를통하여어느특정상황이되었을때 SolidWorks 및보낸경로를통하여알림메시지를보낼수있습니다. 이번기술자료에서는메크로에서이벤트처리기를통하여진행할예정이며, 메크로에서작업을수행하는데유용할것입니다. 알림이벤트핸들러는응용프로그램구현하는데있어서가장중요한부분이라고도할수있기때문입니다. 1. 새로운메크로생성 새메크로만들기버튺을클릭하여파일을생성합니다. 2. 메크로저장 -
More informationPowerPoint Template
7. 상속 (inheritance) 의이해 상속의기본개념 상속의생성자, 소멸자 protected 멤버 Jong Hyuk Park 상속의기본개념 Jong Hyuk Park 상속의기본개념 상속의예 1 " 철수는아버지로부터좋은목소리와큰키를물려받았다." 상속의예 2 "Student 클래스가 Person 클래스를상속한다." 아버지 Person 철수 Stduent 3
More informationC++ Programming
C++ Programming C++ 스타일의입출력 Seo, Doo-okok clickseo@gmail.com http://www.clickseo.com 목 차 C 스타일의입출력 C++ 스타일의입출력 2 C 스타일의입출력 #include C 스타일의입출력 int main() { int a, b, c; printf(" 세개의정수입력 : "); scanf("%d
More information<443A5C4C C4B48555C B3E25C32C7D0B1E25CBCB3B0E8C7C1B7CEC1A7C6AE425CBED0C3E0C7C1B7CEB1D7B7A55C D616E2E637070>
#include "stdafx.h" #include "Huffman.h" 1 /* 비트의부분을뽑아내는함수 */ unsigned HF::bits(unsigned x, int k, int j) return (x >> k) & ~(~0
More informationMicrosoft PowerPoint - chap03-변수와데이터형.pptx
#include int main(void) { int num; printf( Please enter an integer: "); scanf("%d", &num); if ( num < 0 ) printf("is negative.\n"); printf("num %d\n", num); return 0; } 1 학습목표 의 개념에 대해 알아본다.
More informationMicrosoft PowerPoint - chap11-포인터의활용.pptx
#include int main(void) int num; printf( Please enter an integer: "); scanf("%d", &num); if ( num < 0 ) printf("is negative.\n"); printf("num = %d\n", num); return 0; 1 학습목표 포인터를 사용하는 다양한 방법에
More information윈도우시스템프로그래밍
객체지향프로그래밍응용 Chap 4. 대화상자와컨트롤 (#1) 2013.09.27. 오병우 컴퓨터공학과금오공과대학교 Control 들을가진윈도우 Dialog 개요 사용자의입력을받기위한 Object 의집합 종류 프로그램수행도중사용자의입력이필요할때다이얼로그박스출력 다이얼로그박스는사용자로부터입력받은데이터를메인루틴에넘기고소멸 Modal Dialog Parent window
More informationChapter 4. LISTS
C 언어에서리스트구현 리스트의생성 struct node { int data; struct node *link; ; struct node *ptr = NULL; ptr = (struct node *) malloc(sizeof(struct node)); Self-referential structure NULL: defined in stdio.h(k&r C) or
More informationBlog
Objective C http://ivis.cwnu.ac.kr/tc/dongupak twitter : @dongupak 2010. 10. 9.. Blog WJApps Blog Introduction ? OS X -. - X Code IB, Performance Tool, Simulator : Objective C API : Cocoa Touch API API.
More informationMicrosoft PowerPoint - additional03.ppt [호환 모드]
3. 클래스의기본 객체지향프로그래밍소개 구조체와클래스 클래스의정의 Jong Hyuk Park 객체지향프로그래밍소개 Jong Hyuk Park 구조적프로그래밍개념 기존 C와같은구조적프로그래밍언어는동작되는자료와처리동작자체를서로별도로구분 처리동작과자료사이의관계가서로밀접한연관성을갖지못함 프로그램이커지거나복잡해지면프로그램이혼란스럽게되어에러를찾는디버깅및프로그램의유지보수가어려워짐
More information<432B2BC7C1B7CEB1D7B7A1B9D628BABBB9AE5FC3D6C1BE295B315D2E687770>
저 자 약 력이상정순천향대학교컴퓨터학부교수, sjlee@sch.ac.kr 조영일수원대학교컴퓨터학과교수, yicho@suwon.ac.kr 김은성순천향대학교전기전자공학과교수, eskim@sch.ac.kr 박종득공주대학교컴퓨터공학부교수, pjd@kongju.ac.kr C++ 언어는 C 에 C 언어의증가연산자 ++ 를덧붙인 C++ 라는이름이의미하는바와같이 C 언어의문법을대부분그대로사용하면서객체지향프로그래밍기법을추가한
More information1. 상속의기본개념 다음과같은문제를위한클래스설계 자동차 속성 : 색상, 배기량, 현재속도 메서드 : 가속하라, 멈춰라, 시동을켜라 트럭 속성 : 색상, 배기량, 현재속도, 최대중량 메서드 : 가속하라, 멈춰라, 시동을켜라 택시 속성 : 색상, 배기량, 현재속도, 요금,
8 장상속 상속의기본개념상속관련문제제기 base 클래스의접근제어와 protected 멤버상속관계에서의생성자와소멸자함수재정의 (function overriding) 디폴트액세스지정자와구조체 derived 클래스로부터의상속다중상속 virtual base 클래스 derived 클래스의디폴트복사생성자와디폴트대입연산자 private 생성자의사용 C++ 프로그래밍입문
More informationMicrosoft PowerPoint - ch10 - 이진트리, AVL 트리, 트리 응용 pm0600
균형이진탐색트리 -VL Tree delson, Velskii, Landis에의해 1962년에제안됨 VL trees are balanced n VL Tree is a binary search tree such that for every internal node v of T, the heights of the children of v can differ by at
More informationKNK_C_05_Pointers_Arrays_structures_summary_v02
Pointers and Arrays Structures adopted from KNK C Programming : A Modern Approach 요약 2 Pointers and Arrays 3 배열의주소 #include int main(){ int c[] = {1, 2, 3, 4}; printf("c\t%p\n", c); printf("&c\t%p\n",
More information윤성우의 열혈 TCP/IP 소켓 프로그래밍
C 프로그래밍프로젝트 Chap 22. 구조체와사용자정의자료형 1 2013.10.10. 오병우 컴퓨터공학과 구조체의정의 (Structure) 구조체 하나이상의기본자료형을기반으로사용자정의자료형 (User Defined Data Type) 을만들수있는문법요소 배열 vs. 구조체 배열 : 한가지자료형의집합 구조체 : 여러가지자료형의집합 사용자정의자료형 struct
More information비트와바이트 비트와바이트 비트 (Bit) : 2진수값하나 (0 또는 1) 를저장할수있는최소메모리공간 1비트 2비트 3비트... n비트 2^1 = 2개 2^2 = 4개 2^3 = 8개... 2^n 개 1 바이트는 8 비트 2 2
비트연산자 1 1 비트와바이트 비트와바이트 비트 (Bit) : 2진수값하나 (0 또는 1) 를저장할수있는최소메모리공간 1비트 2비트 3비트... n비트 2^1 = 2개 2^2 = 4개 2^3 = 8개... 2^n 개 1 바이트는 8 비트 2 2 진수법! 2, 10, 16, 8! 2 : 0~1 ( )! 10 : 0~9 ( )! 16 : 0~9, 9 a, b,
More informationOCW_C언어 기초
초보프로그래머를위한 C 언어기초 4 장 : 연산자 2012 년 이은주 학습목표 수식의개념과연산자및피연산자에대한학습 C 의알아보기 연산자의우선순위와결합방향에대하여알아보기 2 목차 연산자의기본개념 수식 연산자와피연산자 산술연산자 / 증감연산자 관계연산자 / 논리연산자 비트연산자 / 대입연산자연산자의우선순위와결합방향 조건연산자 / 형변환연산자 연산자의우선순위 연산자의결합방향
More information06장.리스트
---------------- DATA STRUCTURES USING C ---------------- CHAPTER 리스트 1/28 리스트란? 리스트 (list), 선형리스트 (linear list) 순서를가진항목들의모임 집합 : 항목간의순서의개념이없음 리스트의예 요일 : ( 일요일, 월요일,, 토요일 ) 한글자음의모임 : ( ㄱ, ㄴ,, ㅎ ) 카드 :
More informationPowerPoint 프레젠테이션
실습 1 배효철 th1g@nate.com 1 목차 조건문 반복문 System.out 구구단 모양만들기 Up & Down 2 조건문 조건문의종류 If, switch If 문 조건식결과따라중괄호 { 블록을실행할지여부결정할때사용 조건식 true 또는 false값을산출할수있는연산식 boolean 변수 조건식이 true이면블록실행하고 false 이면블록실행하지않음 3
More informationPowerPoint Presentation
public class SumTest { public static void main(string a1[]) { int a, b, sum; a = Integer.parseInt(a1[0]); b = Integer.parseInt(a1[1]); sum = a + b ; // 두수를더하는부분입니다 System.out.println(" 두수의합은 " + sum +
More information프로그램을 학교 등지에서 조금이라도 배운 사람들을 위한 프로그래밍 노트 입니다. 저 역시 그 사람들 중 하나 입니다. 중고등학교 시절 학교 도서관, 새로 생긴 시립 도서관 등을 다니며 책을 보 고 정리하며 어느정도 독학으르 공부하긴 했지만, 자주 안하다 보면 금방 잊어
개나리 연구소 C 언어 노트 (tyback.egloos.com) 프로그램을 학교 등지에서 조금이라도 배운 사람들을 위한 프로그래밍 노트 입니다. 저 역시 그 사람들 중 하나 입니다. 중고등학교 시절 학교 도서관, 새로 생긴 시립 도서관 등을 다니며 책을 보 고 정리하며 어느정도 독학으르 공부하긴 했지만, 자주 안하다 보면 금방 잊어먹고 하더라구요. 그래서,
More information< E20C6DFBFFEBEEE20C0DBBCBAC0BB20C0A7C7D12043BEF0BEEE20492E707074>
Chap #2 펌웨어작성을위한 C 언어 I http://www.smartdisplay.co.kr 강의계획 Chap1. 강의계획및디지털논리이론 Chap2. 펌웨어작성을위한 C 언어 I Chap3. 펌웨어작성을위한 C 언어 II Chap4. AT89S52 메모리구조 Chap5. SD-52 보드구성과코드메모리프로그래밍방법 Chap6. 어드레스디코딩 ( 매핑 ) 과어셈블리어코딩방법
More informationMicrosoft PowerPoint - chap01-C언어개요.pptx
#include int main(void) { int num; printf( Please enter an integer: "); scanf("%d", &num); if ( num < 0 ) printf("is negative.\n"); printf("num = %d\n", num); return 0; } 1 학습목표 프로그래밍의 기본 개념을
More informationNo Slide Title
상속 이충기 명지대학교컴퓨터공학과 상속 Q: 건설회사는기존아파트와조금다르거나추가적인특징들을가진새아파트를지을때어떻게하는가? A: 2 상속 상속 (inheritance) 은클래스들을연관시키는자연스럽고계층적인방법이다. 상속은객체지향프로그래밍의가장중요한개념중의하나이다. 상속은 은 이다 라는관계 (is-a relationship) 를나타낸다. 이관계를적용하여클래스들을상하관계로연결하는것이상속이다.
More information