MFC Console Application 1
콘솔 응용 프로그램 특징 메시지 구동 방식을 사용하지 않으므로 C/C++ 언어에 대한 지식만 있으면 곧바로 실습이 가능하다. 상당수의 MFC 클래스를 사용할 수 있다. 유틸리티 클래스, 집합 클래스, 파일 입출력 클래스, ... 알고리즘을 개발할 때 유용하다. 2
MFC 콘솔 응용 프로그램 작성 (1/2) 프로젝트 생성 3
MFC 콘솔 응용 프로그램 작성 (2/2) 1단계 옵션 설정 4
MFC 콘솔 응용 프로그램 분석 (1/5) 파일 구성 미리 컴파일된 헤더 리소스 정보 구현 코드 StdAfx.h, StdAfx.cpp 미리 컴파일된 헤더 Console.rc, Resource.h 리소스 정보 Console.h, Console.cpp 구현 코드 5
MFC 콘솔 응용 프로그램 분석 (2/5) Console.cpp 코드 CWinApp theApp; // 유일한 전역 응용 프로그램 객체 using namespace std; // 표준 C++ 라이브러리를 사용할 수 있도록 한다. int _tmain(int argc, TCHAR* argv[], TCHAR* envp[]) { int nRetCode = 0; if (!AfxWinInit(::GetModuleHandle(NULL), NULL, ::GetCommandLine( ), 0)) cerr << _T("Fatal Error: MFC initialization failed") << endl; nRetCode = 1; } 6
MFC 콘솔 응용 프로그램 분석 (3/5) Console.cpp 코드 (cont’d) else { CString strHello; strHello.LoadString(IDS_HELLO); cout << (LPCTSTR)strHello << endl; } return nRetCode; 7
MFC 콘솔 응용 프로그램 분석 (4/5) 문자열 리소스 8
MFC 콘솔 응용 프로그램 분석 (5/5) 실행 파일 생성 과정 API & MFC 라이브러리 C/C++ 라이브러리 console.cpp, stdafx.cpp console.h, stdafx.h 컴파일러 console.obj stdafx.obj resource.h console.rc 리소스 각종 리소스 (문자열, 아이콘, ...) console.res 링커 API & MFC 라이브러리 C/C++ 라이브러리 console.exe 9
Utility Classes 10
유틸리티 클래스 (1/3) API 데이터 타입 - 기본형 데이터 타입 BOOL 또는BOOLEAN BYTE, WORD, DWORD, LONG U* HANDLE H* 정의 TRUE 또는 FALSE 8비트, 16비트, 32비트, 32비트(모두 unsigned) unsigned * 예) UCHAR, UINT, ULONG, ... 32비트 핸들 *을 가리키는 핸들 예) HBITMAP(비트맵), HBRUSH(브러시), HCURSOR(커서), HDC(디바이스 컨텍스트), HFONT(폰트), HICON(아이콘), HINSTANCE(인스턴스), HMENU(메뉴), HPEN(펜), HWND(윈도우) 11
유틸리티 클래스 (2/3) API 데이터 타입 - 기본형 데이터 타입 P* = LP* (L)PSTR, (L)PCSTR (L)PTSTR, (L)PCTSTR COLORREF 정의 * 에 대한 포인터 예1) PBOOL, PBYTE, PLONG, ... 예2) LPBOOL, LPBYTE, LPLONG, ... ANSI 문자열 ANSI 또는 유니코드 문자열 32비트 RGB (red, green, blue 각각 8비트) 색상값 12
유틸리티 클래스 (3/3) API 데이터 타입 - 구조체 데이터 타입 정의 용도 POINT typedef struct tagPOINT { LONG x; LONG y; } POINT, *PPOINT; 점의 x, y 좌표(주로 마우스 커서의 위치를 나타낼 때 사용한다.) RECT typedef struct tagRECT { LONG left; LONG top; LONG right; LONG bottom; } RECT, *PRECT; 사각형 왼쪽 상단과 오른쪽 하단 좌표 SIZE typedef struct tagSIZE { LONG cx; LONG cy; } SIZE, *PSIZE; 사각형 폭과 높이 13
CString 클래스 (1/3) 특성 가변 길이 문자열을 지원한다. 프로그램 실행 도중 문자열 길이를 자유롭게 바꿀 수 있다. 최대 길이는 (INT_MAX - 1)이다. const char*나 LPCTSTR 대신 CString 객체를 직접 사용할 수 있다. 이때는 CString 객체에 대해 (LPCTSTR) 연산자를 명시적으로 적용하는 것이 좋다. CString str = "안녕하세요."; cout << (LPCTSTR)str << endl; // 실행 결과? cout << str << endl; // 실행 결과? 14
CString 클래스 (2/3) 객체 생성 및 초기화 CString str1; str1 = "안녕하세요."; CString str4 = str1 + " " + str2 + " 즐거운 날입니다."; cout << (LPCTSTR)str1 << endl; cout << (LPCTSTR)str2 << endl; cout << (LPCTSTR)str3 << endl; cout << (LPCTSTR)str4 << endl; str4 += " 하하하"; 15
CString 클래스 (3/3) CString::Format( ) CString::LoadString( ) CString str; str.Format("x=%d, y=%d", 100, 200); MessageBox(NULL, (LPCTSTR)str, "CString 연습", MB_OK); CString str; str.LoadString(IDS_HELLO); MessageBox(NULL, (LPCTSTR)str, "CString 연습", MB_OK); 16
Windows에서의 문자셋의 종류와 특성 SBCS(Single Byte Character Set) 문자를 표현하는데 1바이트 사용 아스키 코드 MBCS(Multi Byte Character Set) 한글은 2바이트, 영문은 1바이트 사용 WBCS(Wide Byte Character Set) 문자를 표현하는데 2바이트 사용 유니코드 (UNICODE) 17
예제 1] 실행결과 배열의 크기: 8 문자열 길이: 7 int main(void) { char str[] = "ABC한글"; int size = sizeof(str); int len = strlen(str); printf("배열의 크기: %d \n", size); printf("문자열 길이: %d \n", len); return 0; } 실행결과 배열의 크기: 8 문자열 길이: 7 18
예제 2] 실행결과 한글 한글입니다 int main(void) { char str[]="한글입니다"; int i; for(i=0; i<5; i++) fputc(str[i], stdout); fputs("\n", stdout); for(i=0; i<10; i++) return 0; } 실행결과 한글 한글입니다 19
WBCS 기반의 프로그래밍 WBCS를 위한 두 가지 WBCS 기반 문자열 선언 예 첫째: char를 대신하는 wchar_t 둘째: “ABC”를 대신하는 L“ABC” WBCS 기반 문자열 선언 예 wchar_t str[]=L“ABC”; 20
예제 3] WBCS 유니코드 버전 컴파일 오류 int main(void) { wchar_t str[] = L"ABC"; int size = sizeof(str); int len = strlen(str); printf("배열의 크기: %d \n", size); printf("문자열 길이: %d \n", len); return 0; } 21
문자열 조작함수 SBCS 함수 WBCS 기반의 문자열 조작 함수 strlen size_t wcslen (const wchar_t* string); strcpy wchar_t* wcscpy (wchar_t* dest, const wchar_t* src); strncpy wchar_t* wcsncpy (wchar_t* dest, const wchar_t* src, size_t cnt); strcat wchar_t* wcscat (wchar_t* dest, const wchar_t* src); strncat wchar_t* wcsncat (wchar_t* dest, const wchar_t* src, size_t cnt); strcmp int wcscmp (const wchar_t* s1, const wchar_t* s2); strncmp int wcsncmp (const wchar_t* s1, const wchar_t* s2, size_t cnt); 22
예제 4] WBCS 유니코드 버전 실행결과 배열의 크기: 8 문자열 길이: 3 int main(void) { wchar_t str[] = L"ABC"; int size = sizeof(str); int len = wcslen(str); printf("배열의 크기: %d \n", size); printf("문자열 길이: %d \n", len); return 0; } 실행결과 배열의 크기: 8 문자열 길이: 3 23
문자열 입출력 함수 SBCS 함수 WBCS 기반의 문자열 입출력 함수 printf int wprintf (const wchar_t* format [, argument]...); scanf int wscanf (const wchar_t* format [,argument]...); fgets wchar_t* fgetws (wchar_t* string, int n, FILE* stream); fputs int fputws (const wchar_t* string, FILE* stream); 24
예제 5] WBCS 유니코드 버전 매개변수 선언은 어떻게? 실행결과 Array Size : 8 String Length : 3 매개변수 선언은 어떻게? int main(void) { wchar_t str[] = L"ABC"; int size = sizeof(str); int len = wcslen(str); wprintf(L"Array Size : %d \n", size); wprintf(L"String Length : %d \n", len); return 0; } 실행결과 Array Size : 8 String Length : 3 25
매개변수 전달인자 유니코드화 26
예제 6] 유니코드 버전 실행결과 AAA BBB CCC int wmain(int argc, wchar_t* argv[]) { for(int i=1; i<argc; i++) fputws(argv[i], stdout); fputws(L"\n", stdout); } return 0; 실행결과 AAA BBB CCC 27
#include<windows.h> 안에는 WinString.cpp 참조 typedef char CHAR; typedef wchar_t WCHAR; #define CONST const typedef CHAR * LPSTR; typedef CONST CHAR * LPCSTR; typedef WCHAR * LPWSTR; typedef CONST WCHAR * LPCWSTR; 28
MBCS와 WBCS 동시 지원 매크로 #ifdef UNICODE typedef WCHAR TCHAR; typedef LPWSTR LPTSTR; typedef LPCWSTR LPCTSTR; #else typedef CHAR TCHAR; typedef LPSTR LPTSTR; typedef LPCSTR LPCTSTR; #endif 29
MBCS와 WBCS 동시 지원 매크로 #ifdef _UNICODE #define __T(x) L ## x #else #define __T(x) x #endif #define _T(x) __T(x) #define _TEXT(x) __T(x) 30
예제 7] 실행결과 string length : 16 #define UNICODE #define _UNICODE int wmain(void) { TCHAR str[] = _T("1234567"); int size = sizeof(str); printf("string length : %d \n", size); return 0; } 실행결과 string length : 16 31
MBCS와 WBCS 동시 지원 함수 #ifdef _UNICODE #define _tmain wmain #define _tcslen wcslen #define _tprintf wprintf #define _tscanf wscanf #else #define _tmain main #define _tcslen strlen #define _tprintf printf #define _tscanf scanf #endif LPTSTR str1=_T("MBCS or WBCS 1"); TCHAR str2[]=_T("MBCS or WBCS 2"); TCHAR str3[100]; TCHAR str4[50]; LPCTSTR pStr=str1; _tprintf( _T("string size: %d \n"), sizeof(str2)); _tprintf( _T("string length: %d \n"), _tcslen(pStr)); _fputts(_T("Input String 1 : "), stdout); _tscanf(_T("%s"), str3); 32
Character Set 설정 33
CPoint, CRect, CSize 클래스 클래스 정의 업캐스팅 클래스 이름 정의 CPoint class CPoint : public tagPOINT CRect class CRect : public tagRECT CSize class CSize : public tagSIZE void SomeFunc(RECT* rect) { ... } RECT r1; CRect r2; SomeFunc(&r1); // OK! SomeFunc(&r2); // OK! (Upcasting) 34
CPoint 클래스 생성자 예 CPoint::CPoint (int x, int y); CPoint pt1(10, 20); CPoint pt2(pt); pt1.Offset(40, 30); pt2.Offset(20, 10); if(pt1 == pt2) cout << "두 점의 좌표가 같습니다." << endl; else cout << "두 점의 좌표가 다릅니다." << endl; 35
CRect 클래스 (1/2) 생성자 사각형의 폭과 높이 좌표의 포함 여부 판단 CRect::CRect (int l, int t, int r, int b); int CRect::Width (); int CRect::Height (); BOOL CRect::PtInRect (POINT point); 36
CRect 클래스 (2/2) 예 CRect rect1; rect1.SetRect(0, 0, 200, 100); if(rect1 == rect2) cout << "두 사각형의 좌표가 같습니다." << endl; else cout << "두 사각형의 좌표가 다릅니다." << endl; RECT rect = {100, 100, 300, 200}; CRect rect3(rect); cout << rect3.Width() << " " << rect3.Height() << endl; CPoint pt(200, 150); if(rect3.PtInRect(pt)) cout << "점이 사각형 내부에 있습니다." << endl; cout << "점이 사각형 외부에 있습니다." << endl; 37
CSize 클래스 생성자 예 CSize::CSize (int x, int y); CSize size1(100, 200); CSize size2(size); cout << size2.cx << " " << size2.cy << endl; if(size1 == size2) // 두 객체 내용이 같은지 확인한다. cout << "크기가 같습니다." << endl; else cout << "크기가 다릅니다." << endl; 38
CTime, CTimeSpan 클래스 CTime 클래스 CTimeSpan 클래스 절대적인 시간(예를 들면, 현재 시각)을 처리한다. CTimeSpan 클래스 시간의 차이값을 처리한다. 두 클래스 모두 내부적으로 시간값을 64비트로 저장한다. 39
CTime 클래스 예 // 현재 시각 구하기 CTime theTime; theTime = CTime::GetCurrentTime(); // 화면에 출력하기 CString str = theTime.Format("%A, %B %d, %Y"); cout << (LPCTSTR)str << endl; str.Format("현재 시각은 %d시 %d분입니다.", theTime.GetHour(), theTime.GetMinute()); 40
CTimeSpan 클래스 예 // 시간 차 구하기 CTime startTime = CTime::GetCurrentTime(); Sleep(2000); CTime endTime = CTime::GetCurrentTime(); CTimeSpan elapsedTime = endTime - startTime; CString str; str.Format("%d초가 지났습니다.", elapsedTime.GetTotalSeconds()); cout << (LPCTSTR)str << endl; 41
배열 클래스 (1/7) MFC 배열 클래스의 특징 템플릿 클래스 배열 인덱스를 잘못 참조하는 경우 오류를 발생시킨다. 배열 크기가 가변적이다. 템플릿 클래스 afxtempl.h 헤더 파일 클래스 이름 데이터 타입 사용 예 CArray 프로그래머가 결정 CArray<CPoint, CPoint&> array; 42
배열 클래스 (2/7) 비 템플릿 클래스 afxcoll.h 헤더 파일 클래스 이름 데이터 타입 사용 예 CByteArray CByteArray array; CWordArray WORD CWordArray array; CDWordArray DWORD CDWordArray array; CUIntArray UINT CUIntArray array; CStringArray CString CStringArray array; CPtrArray void 포인터 CPtrArray array; CObArray CObject 포인터 CObArray array; 43
배열 클래스 (3/7) 생성과 초기화 CStringArray array; array.SetSize(5); for(int i=0; i<5; i++){ CString string; string.Format("%d년이 지났습니다.", i*10); array[i] = string; } for(i=0; i<5; i++) cout << (LPCTSTR)array[i] << endl; 44
배열 클래스 (4/7) 원소 삽입과 삭제 CUIntArray array; array.SetSize(5); for(int i=0; i<5; i++) array[i] = i; // 배열 원소 삽입 array.InsertAt(3, 77);\ for(i=0; i<array.GetSize(); i++) cout << array[i] << endl; cout << endl; // 배열 원소 삭제 array.RemoveAt(3); 45
배열 클래스 (5/7) 템플릿 배열 클래스 #include "stdafx.h" #include "Console.h" #include <afxtempl.h> CWinApp theApp; using namespace std; struct Point3D { int x, y, z; Point3D() {} Point3D(int x0, int y0, int z0) { x = x0; y = y0; z = z0; } }; 46
배열 클래스 (6/7) int _tmain(int argc, TCHAR* argv[ ], TCHAR* envp[ ]) { int nRetCode = 0; if (!AfxWinInit(...)) // 생략 ... } else CArray<Point3D, Point3D&> array; array.SetSize(5); for(int i=0; i<5; i++) { Point3D pt(i, i*10, i*100); array[i] = pt; 47
배열 클래스 (7/7) for(i=0; i<5; i++) { Point3D pt = array[i]; cout << pt.x << ", " << pt.y << ", " << pt.z << endl; } return nRetCode; 48
리스트 클래스 (1/8) MFC 리스트 클래스 템플릿 클래스 afxtempl.h 헤더 파일 클래스 이름 데이터 타입 사용 예 Head Tail 클래스 이름 데이터 타입 사용 예 CList 프로그래머가 결정 CList<CPoint, CPoint&> list; 49
리스트 클래스 (2/8) 비 템플릿 클래스 afxcoll.h 헤더 파일 클래스 이름 데이터 타입 사용 예 CObList CObject 포인터 CObList list; CPtrList void 포인터 CPtrList list; CStringList CString CStringList list; 50
리스트 클래스 (3/8) 생성과 초기화 char *szFruits[ ] = { "사과", "딸기", "포도", "오렌지", "자두" }; CStringList list; for(int i=0; i<5; i++) list.AddTail(szFruits[i]); 51
리스트 클래스 (4/8) 순환 // 리스트 제일 앞에서 출발하여 순환한다. POSITION pos = list.GetHeadPosition(); while(pos != NULL){ CString string = list.GetNext(pos); cout << (LPCTSTR)string << endl; } cout << endl; // 리스트 제일 뒤에서 출발하여 순환한다. pos = list.GetTailPosition(); CString string = list.GetPrev(pos); 52
리스트 클래스 (5/8) 항목 삽입과 삭제 // POSITION 타입의 변수 pos는 이전의 예제에서 선언한 것이다. pos = list.Find("포도"); list.InsertBefore(pos, "살구"); list.InsertAfter(pos, "바나나"); list.RemoveAt (pos); // 항목 삽입과 삭제 후 결과를 확인한다. pos = list.GetHeadPosition(); while(pos != NULL){ CString string = list.GetNext(pos); cout << (LPCTSTR)string << endl; } 53
리스트 클래스 (6/8) 템플릿 리스트 클래스 #include "stdafx.h" #include "Console.h" #include <afxtempl.h> CWinApp theApp; using namespace std; struct Point3D { int x, y, z; Point3D() {} Point3D(int x0, int y0, int z0) { x = x0; y = y0; z = z0; } }; 54
리스트 클래스 (7/8) int _tmain(int argc, TCHAR* argv[ ], TCHAR* envp[ ]) { int nRetCode = 0; if (!AfxWinInit(...)) // 생략 ... } else CList<Point3D, Point3D&> list; for(int i=0; i<5; i++) list.AddTail(Point3D(i, i*10, i*100)); POSITION pos = list.GetHeadPosition(); 55
리스트 클래스 (8/8) while(pos != NULL) { Point3D pt = list.GetNext(pos); cout << pt.x << ", " << pt.y << ", " << pt.z << endl; } return nRetCode; 56
맵 클래스 (1/9) 맵 동작 원리 MFC 맵 클래스 구현 키 데이터 해시 함수 키 데이터 57
맵 클래스 (2/9) 템플릿 클래스 afxtempl.h 헤더 파일 클래스 이름 데이터 타입 사용 예 CMap 프로그래머가 결정 CMap<CString, CString&, CPoint, CPoint&> map; 58
맵 클래스 (3/9) 비 템플릿 클래스 afxcoll.h 헤더 파일 클래스 이름 키 데이터 사용 예 CMapWordToOb WORD CObject 포인터 CMapWordToOb map; CMapWordToPtr WORD void 포인터 CMapWordToPtr map; CMapPtrToWord void 포인터 WORD CMapPtrToWord map; CMapPtrToPtr void 포인터 void 포인터 CMapPtrToPtr map; CMapStringToOb 문자열 CObject 포인터 CMapStringToOb map; CMapStringToPtr 문자열 void 포인터 CMapStringToPtr map; CMapStringToString 문자열 문자열 CMapStringToString map; 59
맵 클래스 (4/9) 생성, 초기화, 검색 CMapStringToString map; map["사과"] = "Apple"; map["딸기"] = "Strawberry"; map["포도"] = "Grape"; map["우유"] = "Milk"; CString str; if(map.Lookup("딸기", str)) cout << "딸기 -> " << (LPCTSTR)str << endl; 60
맵 클래스 (5/9) 순환 POSITION pos = map.GetStartPosition(); while(pos != NULL){ CString strKey, strValue; map.GetNextAssoc(pos, strKey, strValue); cout << (LPCTSTR)strKey << " -> " << (LPCTSTR)strValue << endl; } 61
맵 클래스 (6/9) 삽입과 삭제 map.RemoveKey("우유"); map["수박"] = "Watermelon"; // 항목 삽입과 삭제 후 결과를 확인한다. // POSITION 타입의 변수 pos는 이전의 예제에서 선언한 것이다. pos = map.GetStartPosition(); while(pos != NULL){ CString strKey, strValue; map.GetNextAssoc(pos, strKey, strValue); cout << (LPCTSTR)strKey << " -> " << (LPCTSTR)strValue << endl; } 62
맵 클래스 (7/9) 템플릿 맵 클래스 #include "stdafx.h" #include "Console.h" #include <afxtempl.h> CWinApp theApp; using namespace std; UINT AFXAPI HashKey(CString& str) { LPCTSTR key = (LPCTSTR) str; UINT nHash = 0; while(*key) nHash = (nHash<<5) + nHash + *key++; return nHash; } 63
맵 클래스 (8/9) int _tmain(int argc, TCHAR* argv[ ], TCHAR* envp[ ]) { int nRetCode = 0; if (!AfxWinInit(...)) // 생략 ... } else CMap<CString, CString&, UINT, UINT&> map; map[CString ("사과")] = 10; map[CString ("딸기")] = 25; map[CString ("포도")] = 40; map[CString ("수박")] = 15; 64
맵 클래스 (9/9) UINT nCount; if(map.Lookup(CString("수박"), nCount)) cout << "수박 " << nCount << "상자가 남아있습니다." << endl; } return nRetCode; 65