// 마치 문자열만 전문적으로 처리하는 포인터와 같다.
#include <iostream>
#include <vector>
#include <string>
#include <string_view>
class MyString
{
public:
static char SSO[25];
/*
Small/Short String Optimization
25글자 이하는 동적할당이 아니라 전역을 통해 해결하기도 한다.
하지만 그럼에도 string_view가 빠르다(?).
*/
char* Test;
MyString(const char* _Ptr)
{
Test = new char[strlen(_Ptr)]; // 동적할당
}
};
class MyString_View
{
public:
const char* Test;
/*
string_view는 포인터 역할일 뿐, 실제 데이터를 갖고 있지는 않다.
게다가 const나 마찬가지기 때문에 내부의 내용을 변경하는 것도 불가하다.
*/
MyString_View(const char* _Ptr)
{
Test = _Ptr; // 얕은 복사
}
};
void FunctionString(const MyString& _Value)
{
}
void FunctionView(MyString_View Name)
{
/*
Name.data();
.data()의 역할...?
*/
}
int main()
{
FunctionString("AgjksflhgjsklhgsdfjkAAAA");
/*
MyString NewString = "AgjksflhgjsklhgsdfjkAAAA";
FunctionString(NewString);
실제론 위와 같이 새로운 메모리에 할당이 발생한다.
함수가 종료되면 할당된 메모리의 삭제도 필요하다.
따라서, const std::string& 으로 받는 것보다, string_view 로 받으면
해당 값의 위치를 반환받을 수 있어 연산이 줄어들고 빠르다.
*/
}
#include "framework.h"
#include "WindowsProject1.h"
#define MAX_LOADSTRING 100
// 전역 변수 :
// [HINSTANCE = 프로그램 핸들]
HINSTANCE hInst; // 현재 인스턴스
WCHAR szTitle[MAX_LOADSTRING]; // 제목 표시줄 텍스트
WCHAR szWindowClass[MAX_LOADSTRING]; // 기본 창 클래스 이름
bool Live = true;
// 함수 선언 :
ATOM MyRegisterClass(HINSTANCE hInstance);
BOOL InitInstance(HINSTANCE, int);
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
INT_PTR CALLBACK About(HWND, UINT, WPARAM, LPARAM);
// 진입점 :
int APIENTRY wWinMain(_In_ HINSTANCE hInstance,
_In_opt_ HINSTANCE hPrevInstance,
_In_ LPWSTR lpCmdLine,
_In_ int nCmdShow)
{
// 사용하지 않은 인자 사용
UNREFERENCED_PARAMETER(hPrevInstance);
UNREFERENCED_PARAMETER(lpCmdLine);
// 전역 문자열 초기화
LoadStringW(hInstance, IDS_APP_TITLE, szTitle, MAX_LOADSTRING);
LoadStringW(hInstance, IDC_WINDOWSPROJECT1, szWindowClass, MAX_LOADSTRING);
MyRegisterClass(hInstance);
// 애플리케이션 초기화
if (!InitInstance (hInstance, nCmdShow))
{
return FALSE;
}
HACCEL hAccelTable = 0; // 단축키 (사용X)
MSG msg;
// 기본 메시지 루프
while (Live)
// while (GetMessage(&msg, nullptr, 0, 0))
{
/*
GetMessage()
- 메세지가 없으면 프로그램을 정지시키고, 메세지가 올 때까지 대기한다.
PeekMesaage()
- 메세지가 없으면 return
PM_REMOVE
- 메세지 버퍼 삭제
- 한번 실행되면 누적된 메세지를 삭제하여 마지막에 전해진 메세지만 실행한다
PM_NOREMOVE
- 윈도우 메세지를 지속적으로 삭제하지 않기 때문에 계속 쌓인다
- 너무 많이 쌓이게 되면 윈도우가 정지한 것처럼 보일 수 있다
*/
if (PeekMessage(&msg, nullptr, 0, 0, PM_REMOVE))
// if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg))
// 게임에서는 단축키를 사용하지 않기 때문에 단축키를 처리하는 구문은 필요 없다.
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
// (TODO)
/*
(Tip?) 만약 인자를 이해할 수 없다면, 대부분의 함수에서
0을 넣으면 디폴트 옵션으로 실행시켜준다(...).
*/
}
return (int) msg.wParam;
}
// 창 클래스 등록 :
ATOM MyRegisterClass(HINSTANCE hInstance)
{
/*
앞이나 뒤에 W 또는 A가 붙어있는 함수
- A => 멀티바이트 함수
- W => 와이드바이트 함수
따라서 이 경우, wcex.lpszClassName을 직접 입력해주고 싶다면 "" 앞에 L을 붙여야 한다.
*/
WNDCLASSEXW wcex;
wcex.cbSize = sizeof(WNDCLASSEX);
wcex.style = CS_HREDRAW | CS_VREDRAW;
wcex.lpfnWndProc = WndProc; // 메세지 콜백 (함수포인터, 다형성)
wcex.cbClsExtra = 0;
wcex.cbWndExtra = 0;
wcex.hInstance = hInstance;
wcex.hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_WINDOWSPROJECT1));
wcex.hCursor = LoadCursor(nullptr, IDC_ARROW);
wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);
wcex.lpszMenuName = nullptr; // 상단메뉴 (사용X)
wcex.lpszClassName = szWindowClass;
wcex.hIconSm = LoadIcon(wcex.hInstance, MAKEINTRESOURCE(IDI_SMALL));
// 윈도우 창 클래스 만들기
return RegisterClassExW(&wcex);
}
// 인스턴스 핸들 저장, 주 창 만들기 :
BOOL InitInstance(HINSTANCE hInstance, int nCmdShow)
{
// 인스턴스 핸들
hInst = hInstance;
// 윈도우 창 생성
// [HWND = 윈도우 핸들]
HWND hWnd = CreateWindowW(szWindowClass, szTitle, WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, nullptr, nullptr, hInstance, nullptr);
/*
szTitle => const char* 형
만약 const std::string& _Title 을 인자로 넣고 싶다면...
const std::string& 은 내부에 std::vector<char> 이 존재한다고 생각하면 된다.
&_Title[0]
_Title.c_str()
...혹은 조금 다르지만 std::string_view _Title 을 사용할 수 있다!
_Title.data()
*/
if (!hWnd)
{
return FALSE;
}
// 윈도우 창 띄우기
ShowWindow(hWnd, nCmdShow);
UpdateWindow(hWnd);
return TRUE;
}
// 주 창 메시지 처리 :
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
switch (message)
{
/*
// 애플리케이션 메뉴 처리
case WM_COMMAND:
{
int wmId = LOWORD(wParam);
// 메뉴 선택을 구문 분석
switch (wmId)
{
case IDM_ABOUT:
DialogBox(hInst, MAKEINTRESOURCE(IDD_ABOUTBOX), hWnd, About);
break;
case IDM_EXIT:
DestroyWindow(hWnd);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
}
break;
게임에서는 상단 메뉴를 사용하지 않기 때문에 삭제해도 된다.
*/
// 주 창 그리기
case WM_PAINT:
{
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hWnd, &ps);
// [HDC = 윈도우에 그림을 그릴 수 있는 권한]
Rectangle(hdc, 100, 100, 200, 200); // (왼쪽 위, 오른쪽 아래)
EndPaint(hWnd, &ps);
}
break;
// 종료 메세지 게시하고 반환
case WM_DESTROY:
PostQuitMessage(0);
/*
GetMessage()는 평소에 1을 return한다.
PostQuitMessage()가 호출되면 GetMessage()가 0을 호출해서 while문에서 탈출한다.
*/
Live = false;
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
/*
사용자가 할일을 정의하지 않은 메세지들은 윈도우가 디폴트로 처리하는 방식으로 실행시켜준다.
WM_SETFOCUS, WM_KILLFOCUS, ... 등 여러 종류의 메세지가 존재한다.
*/
}
return 0;
}
// 정보 대화 상자 메시지 처리기 :
INT_PTR CALLBACK About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
UNREFERENCED_PARAMETER(lParam);
switch (message)
{
case WM_INITDIALOG:
return (INT_PTR)TRUE;
case WM_COMMAND:
if (LOWORD(wParam) == IDOK || LOWORD(wParam) == 2)
{
EndDialog(hDlg, LOWORD(wParam));
return (INT_PTR)TRUE;
}
break;
}
return (INT_PTR)FALSE;
}
/*
HINSTANCE => 프로그램 핸들
e.g. 내 프로그램이 4800번일 경우
윈도우에게 4800번 프로그램이 창 하나 만들어달라고 전달
윈도우에게 4800번 프로그램이 키보드 연결을 끊고 싶다고 전달
HWND => 윈도우 핸들
e.g. 윈도우에게 4800번 프로그램의 530번 창 크기를 바꾸고 싶다고 전달
HDC => 윈도우에 그림을 그릴 수 있는 권한
e.g. 윈도우에게 4800번 프로그램의 530번 창의 230번 그림권한에서 사각형을 그리고 싶다고 전달
HMENU, HCURSOR, HICON, ... 등 여러 권한이 존재한다.
*/
$(SolutionDir)Bin\$(Platform)\$(Configuration)\
$(SolutionDir)Bin\$(Platform)\$(Configuration)\$(ProjectName)\
Q) bin을 따로 설정해주는 이유?
출력된 요소들을 한곳으로 모으기 위해
..\;
추가Level0 : EngineBase
Level1 : EnginePlatform
Level2 : EngineCore
Level3 : Contents
Level4 : App