2012년 8월 16일 목요일

unity action script 메모

이동을 멈추게 하고 싶을떄

naviMeshAgent.destination = transform.position;

이런 식으로는 의도하는대로 작동하지 않는다.

naviMeshAgent.ResetPath();

를 사용할것.

다시 움직이게 하려면 destination을 다시 지정해주면 된다.

2012년 6월 27일 수요일

[2.5D & 서버 개인 포트폴리오] 네트워크 FPS





- 3D 과정 1개월차

빌보드 구현, 높이맵 적용, 스프라이트 애니메이션



- 서버 프로그래밍(독학)

TCP/IP 와 쓰레드를 이용한 동기화 PVP 구현



.

2012년 6월 22일 금요일

오늘의 글귀..


가르치되 무시하지 않도록

비판하되 비난하지 않도록

솔직하되 품위를 잃지 않도록

자신감을 잃지 않되 자만으로 빠지지 않도록

겸손하되 비굴하지 않도록

사랑하되 집착하지 않도록


공감하되 동정하지 않도록





2012년 6월 18일 월요일

[Direct3D] 렌더링 파이프라인

렌더링 파이프라인이란?

3D 공간 상에 기하학적으로 구성된 장면에 가상 카메라를 설정하고 모니터에 2D로 표현하기까지의 일련의 과정을 랜더링 파이프라인이라고 한다.


일반적으로 아래의 9 단계로 이루어진다.

1. 로컬 스페이스
2. 월드 스페이스
3. 뷰 스페이스
4. 후면 추려내기(컬링)
5. 조명
6. 클리핑
7. 투영
8. 뷰포트 변환
9. 래스터라이징




1. 로컬 스페이스
모델링 스페이스라고도 하며 물체를 구성하는 삼각형(폴리곤) 목록을 정의하는 좌표계이다. 3D 물체를 월드 스페이스에 직접 배치하는 것보다 각각의 로컬 스페이스에 정의하는 것이 훨씬 효율적이다.



2. 월드 스페이스
로컬 스페이스의 물체들은 이동, 회전, 크기변형 등을 포함하는 월드 변환 작업을 거쳐 월드 스페이스(전역좌표계)로 옮겨진다. 월드 변환은 각 물체간의 상대적 관계를 정의하여 하나의 장면(scene)을 구성하는 것이다.



3. 뷰 스페이스
카메라의 관점에 맞추어 월드 내의 모든 기하물체를 변환하는 것을 뷰 스페이스 변환이라고 한다. 이 변환을 거친 뒤의 기하물체는 뷰 스페이스 내에 위치한다고 표현한다.
즉, 월드 내의 어떤 위치에 존재하는 카메라를 원점으로 옮기고, 양의 z 축 방향으로 바라보도록(한마디로 컴퓨터 모니터가 카메라 위치가 되도록) 회전과 이동을 하며, 월드내의 모든 물체도 그에 따라 변환되는 것이다.
뷰 스페이스는 2D 모니터로 Scene을 찍기 위한 중간과정이라 보면 되고 게임에서 각 물체간의 위치관계나 충돌 처리 등은 주로 월드 스페이스에서 이루어진다.



4. 후면 추려내기(backface culling)
카메라에서 보이지 않는 폴리곤의 후면을 이후의 처리에서 제거(추려내기)하는 작업이다.
D3D에서 삼각형의 버텍스는 시계방향 순서로 배치하도록 약속되어 있다. 이 삼각형이 180도 회전하여 후면으로 가게되면 버텍스 순서가 시계반대방향으로 뒤집어지는데, 이러한 삼각형들이 바로 컬링의 대상이 되는 것이다.

※ D3DRS_CULLMODE 옵션을 이용하여 동작을 변경할 수 있다.

    Device->SetRenderState(D3DRS_CULLMODE, Value);

Value 에는 다음의 세 가지 중 하나를 선택적으로 사용할 수 있다.

 D3DCULL NONE - 후면 추려내기 기능을 사용하지 않는다.
 D3DCULL CW     - 시계 방향(ClockWise) 두르기를 가진 삼각형을 추려낸다
 D3DCULL CCW   - 시계 반대 방향(CounterClockWise) 두르기를 가진 삼각형을 추려낸다. 디폴트 상태이다.



5. 조명
월드에 광원을 배치하여 물체에 명암 효과를 준다.



6. 클리핑
시야 볼륨(절두체) 외부의 기하물체를 추려내는 과정이다.
절두체란 시야의 각도와 최소깊이, 최대깊이로 이루어진 피라미드 형태로서 이 안에 있는 물체가 카메라의 시야 내부에 있게되는 것이다.



7. 투영
3D 장면에서 2D 표현을 얻는 과정이다. 원근 투영(perspective projection)이나 직교 투영(orthogonal projecton) 등이 있다.
원근 투영은 원근법을 살려서 가까운 물체는 크게, 먼 물체는 작게 보이도록 변환하는것으로 위에서 설명한 절두체를 2D 윈도우에 투영하는 것이다.
직교 투영은 원근감없이 수직 위에서 내려다 보듯이 투영하는 방법이며 게임에서는 미니맵 등의 2D 효과에 사용된다.



8. 뷰포트 변환
투영된 삼각형들을 뷰포트라 불리는 윈도우 좌표계로 변환하는 과정이다.
이때 z축 버퍼의 개념이 사용되어 멀리 있는 물체가 가까운 물체에 가리는 등의 처리가 이루어진다.
D3D에서는 뷰포트 변환이 자동으로 이루어진다.



9. 래스터라이즈
뷰포트 변환을 통해 2D 삼각형들의 리스트를 가지게 되는데 이 삼각형들을 그리는데 필요한 픽셀 컬러를 계산하는 과정을 래스터라이즈라고 한다.
지금까지는 버텍스 단위로 작업이 이루어졌지만 래스터라이즈 단계는 픽셀 단위로 작업을 해야하므로 상당한 부하가 걸린다. 따라서 반드시 전용 그래픽 하드웨어에서 처리되어야한다.
래스터라이즈의 결과물은 모니터에 바로 디스플레이할 수 있는 2D 이미지가 된다.




※ 위 랜더링 파이프라인은 3D 장면을 2D 화면으로 전환하는 최소의 과정만을 나타낸 것이라고 볼 수 있으며 실제로는 버텍스 셰이딩(정점의 음영처리), 지오메트리 셰이딩(정점 증감 처리), 픽셀 셰이딩, 텍스쳐 적용, 렌더 백엔드 등의 과정이 추가될 수 있다.

2012년 6월 15일 금요일

[STL] list iterator not dereferencable 오류 메시지

 위 메시지에서 dereferencable 이라는 사전에도 잘 안나오는 단어는 역참조라는 뜻인데, 이걸 해석하느라 고심할 필요는 없고 한마디로 STL에서 이터레이터로 루프를 돌릴때 로직이 잘못되었다고 보면 된다.


 흔히 범하는 실수로는 반복자 삭제시 무효화 현상, 루프를 돌릴때 조건을 잘못 주는 등 여러가지가 있다.

 이런 오류는 컴파일러에서 발견하지 못하고 실행중에 발생하므로 찾기가 어려운 편이다.

 이런 경우 디버그를 할 때 반복자 관련 구문을 잘 살펴보면 된다.



list 뿐 아니라 vector 나 map 도 마찬가지다.




2012년 5월 22일 화요일

[DirectX 3D] DirectX SDK 설치와 작업환경 설정하기

다운받은 다이렉트X SDK를 압축을 풀어서 실행합니다.
참고로 저는 2007 11월 버전을 설치했습니다.







두 개 옵션에 체크한 상태로 Unzip 클릭하면 압축이 풀리고
자동으로 셋업이 시작됩니다.







설치해주시고








설치가 완료되었으면 비주얼 스튜디오에서 도구 > 옵션 선택하세요.
그리고 아래 단계를 따라주세요.
(참고로 저의 설치환경은 VS 2008 버전입니다.)

1. 프로젝트 및 솔루션 선택
2. VC++ 디렉터리
3. 포함(인클루드) 파일
4. 그림 클릭하여 새 줄 삽입
5. SDK가 설치된 폴더의 include 경로로 설정해줌.

※그림 클릭하면 커집니다







같은 방식으로 라이브러리 파일 경로도 설정해준 뒤
(여기서는 32비트 개발환경이므로 x86 디렉토리를 선택했음)
확인을 클릭합니다.







이제 필요한 헤더와 라이브러리를 다음과 같이 포함시켜
프로젝트를 작성하면 됩니다.




2012년 5월 2일 수요일

사람을 다루는 기본적인 기술

1. 상대방에게 비판, 비난, 불평을 하지 마라.
 무엇이 옳고 그른것과는 상관없이 자존심에 상처를 입은 상대는 그대의 적이 되거나 자기합리화로 자신의 자존감을 지키려 할 뿐이다. 이러한 방식으로는 궁극적으로 사람을 설득할 수 없다.


2. 솔직하고 진지하게 칭찬하라.
아첨과 칭찬을 구분할 줄 알아야한다. 현명한 사람에게 아첨은 먹히지 않는다.


3. 다른 사람들의 강한 욕구를 불러일으켜라.
 누구나 자신의 욕구에만 관심이 있을뿐 타인의 욕구에는 관심이 없다. 만약 당신이 타인이 원하는 것에 신경을 쓰는 버릇을 들인다면 사람을 움직이고 설득할 수 있게 된다. "우선 무엇보다도 상대방의 강한 욕구를 불러일으켜라. 이대로 하는 자가 세상을 얻을 것이다. 그렇지 않은 자는 외로운 길을 걸을 것이다."



 from 데일카네기의 인간관계론 1장


......이렇게 잘 다루면 된다^^



2012년 5월 1일 화요일

[2D 팀 포트폴리오] 액션 RPG 던파






작업기간 보름 중 실시간 그림자를 구현하는데만 이틀,
거기다 버그 잡는데만 하루 정도는 사용한 것 같다.

그리고 만들고나니 보기보다 고사양 게임이 되고 말았는데
커다란 비트맵을 한번에 로딩하면 성능이 급격히 떨어진다는 사실도 깨닫게 되었다.






API에서 구현한 실시간 그림자 기능

[2D 팀 포트폴리오] 종스크롤 슈팅





왠지 2D 슈팅게임 만드는 회사는 지금이라도 들어가서 일할 수 있을것같다^^;


2D 액션 RPG 팀작업 후기..

- 맵 전환시 기존 맵을 먼저 지우고 다음 맵을 로딩하는 방식을 사용했더니 부하가 크게 줄었다. 원래는 약간 위험할것 같아서 먼저 로딩해두고 삭제하는 방식을 사용하고 있었는데 순서를 바꿨더니 정확한 이유는 모르겠지만 눈에 띄게 성능이 개선되었다.


- 리소스 이미지 크기가 수천x수천픽셀 정도로 커지면 성능이 눈에 띄게 저하된다. 왠만한 3D게임이 돌아가는 시스템에서도 이미지 로딩이 실패할 정도의 고사양 게임이 되고 만다. 실무에서는 최대 258이나 512사이즈의 이미지로 쪼개서 사용한다고 한다.


- 또한 이미지는 화면 밖의 범위는 출력되지 않도록 클리핑 영역 설정을 해줘야한다. 조건문이 몇개 추가되는 것이 광범위한 픽셀 복사 연산을 발생시키는 것보다는 훨씬 효율적이다.


- 스레드를 빨리 공부해야겠다. 싱글코어는 한계가 많다...


- 다양한 디자인 패턴을 진작 공부해둘걸... 싱글턴과 스테이트만 쓰는 것도 이제 지겹다.


- 팀작업이란 멘탈적으로 힘든 작업이다. 특히 리더는 작업내용의 모든걸 파악해야하고 팀원들의 사기를 북돋아줄 수 있어야한다.


......?

2012년 4월 16일 월요일

[Windows API] 유니코드 문자열 관련 간단 정리


아래 방식을 사용하면 유니코드와 아스키 코드 양쪽을 받아주는 호환성있는 코드가 된다.




TCHAR 타입: wchar_t 또는 char 자동으로 받아준다.


TEXT("문자열")





그리고 아래는 유니코드 방식의 문자열을 쓰는 방식이다.

L"문자열" 

[Windows API] 다이얼로그 박스의 차일드 컨트롤 깜빡임 제거하기

일반 윈도우의 차일드 컨트롤의 경우

메인 윈도우를 생성할때 WS_CLIPSIBLINGS | WS_CLIPCHILDREN 속성을 부여하면

차일드 윈도우를 매번 다시 그리지 않게 되므로 깜빡임을 제거할 수 있다.



그런데 다이얼로그 박스의 경우 CreateWindow 함수를 사용해서 생성하지 않으므로

생성시 속성을 부여할 방법이 없다.

일반적으로 윈도우 깜빡임을 해결하는 방법은 더블 버퍼링이지만 컨트롤은 그렇게 할 수도 없다(방법이 있을지도 모르지만 필자는 모르겠다).

때문에 다이얼로그 박스 내부의 컨트롤은 계속 깜빡거리고 있게된다.



하지만 비주얼 스튜디오의 리소스 편집기에서 속성을 변경하면 문제가 간단히 해결된다.

리소스 편집기로 대화상자 리소스의 속성창을 열어서 아래 부분을 TRUE 로 바꿔주면 된다.






-------------------
12.04.17 내용추가

SetWindowLong 함수를 사용해 윈도우 속성을 변경해줄 수 있다고 합니다.

이쪽이 더 근본적인 해결책이겠네요^^







2012년 3월 26일 월요일

[WINAPI] 이미지 출력 함수 GdiTransparentBlt(), GdiAlphaBlend()

아래 두 함수는 비트맵의 일부를 투명하게 출력하는데 이용되는 함수들이다.



TransparentBlt()

AlphaBlend()



자세한 사용법에 대해서는 다른 블로그나 책에 많이 나와있으니 생략하고...

위 두 함수를 사용하려면 MsImg32.lib 라이브러리를 링크시켜 사용해야 한다.

그런데 WINAPI를 공부하면서 위 함수들에 대해서 찾다가 이런 것들을 발견했다.



GdiTransparentBlt()

GdiAlphaBlend()



보는 바와 같이 앞에 Gdi 가 붙은 형태이다.

이 함수들은  윈도우 2000 버전 이상의 Gdi32.lib 에 정의되어 있고 받는 인자나 사용법, 기능 등은 Gdi가 안붙은 버전과 완전히 동일하다.

다만 특별한 라이브러리 추가 없이 Windows.h를 인클루드 하면 그냥 사용가능하다는 것이 가장 큰 차이점이다.

단점은 윈도우 98 이하에서는 위 함수를 사용한 코드가 호환되지 않는다는 점이다.





또 다른 Gdi 형제로는 GdiGradientFill() 이라는 함수도 있다.

기존 FloodFill() 함수나 윈도우가 기본 제공하는 브러시, 펜 등을 이용해서는 단색 밖에 그릴 수가 없으므로 그라데이션으로 색을 칠할 수 있다고 하니 처음 함수 이름을 봤을때는 대박이라고 생각했다.

다만 자세히 들여다보니 아쉽게도 삼각형과 사각형 만을 지원한다고 하여 실망했던 기억이 있다.

몇개의 점 좌표와 각 점에 RGB 값을 인자로 주면 아래처럼 그라데이션 색으로 채워진 삼각형이나 사각형을 그려주는 방식이다.






개인적으로는 그다지 활용방법이 떠오르지 않아서 더이상 깊이는 조사해보지 않았다.

관심이 있다면 MSDN에서 검색해보자. (영어로) 자세히 설명되어 있다.




참고로 이 함수들을 한국어로 된 기존 책이나 블로그에서는 본 기억이 없다.

아마도 이 글이 최초 소개가 아닐까 싶다.


2012년 3월 21일 수요일

역시 언제나 인간관계가 가장 힘든것같다...

명확한 답도 없는것같고...


중요한건,

어느 정도 상황에 맞춰가는것도 있어야 하겠지만

이게 아니다 싶은 경우에는 자기 중심을 지켜야한다는것.

색깔있는 사람이 멋진거다...


그리고 단점이 아니라 장점에 주력하자...

내가 능력있고 성공하면

사실 이런 고민들도 저절로 해결될 문제일것이다......








2012년 3월 17일 토요일

[C++][WINAPI] GetKeyState()와 GetAsyncKeyState()의 차이점


예전에 인터넷에서 두 함수의 차이점을 검색해보았을때 잘못된 정보로 인해 혼란을 겪은 적이 있기 때문에 직접 정리해보기로 하였다.

일단 두 함수의 설명을 MSDN에서 찾아서 직접 번역해보았다.
이해하기 쉽도록 약간 의역 + 수정하였음을 밝혀둔다.


1.

SHORT GetKeyState( 
   int nVirtKey 
); 


이 함수는 특정 가상키의 상태를 찾아온다(retrieves). 키의 상태란 UP / DOWN / 토글 ON / OFF 상태를 말한다.

리턴값은,
키가 DOWN(눌린) 상태일때 최상위비트(high-order bit, 0x8000)가 1이고, UP이면 0이다.
토글 ON 이면 최하위비트(high-order bit, 0x0001)가 1이고, OFF이면 0이다.
(※ 참고로 GetKeyState는 Num Lock, Caps Lock 같은 토글키 이외에도 모든 키에 대하여 누를때마다 리턴값이 1과 0으로 토글된다.)

이 함수가 리턴하는 키의 상태는 특정 스레드가 그 메시지 큐로부터 키 메시지를 읽으면 변한다. 즉, 하드웨어 인터럽트 레벨에서의 키 상태를 반영하지는 못한다. 더 즉각적인 정보를 위해서는 GetAsyncKeyState 함수를 이용하는 것이 좋다.

응용프로그램은 키보드 입력 메시지에 대하여 GetKeyState를 호출한다. 이 함수는 입력 메시지가 생성되었을 때의 키의 상태를 반영한다.



2.

SHORT GetAsyncKeyState( 
  int vKey 
); 

이 함수는 함수 호출 시점에 특정키가 눌렸는지를 확인하며(determine) 바로 전의 호출과 이번 호출 사이에 키가 눌린 적이 있는지도 확인해준다.

키가 눌린 상태이면 최상위비트(most significant bit, 0x8000)가 셋(1)되고, 바로 전 호출 이후에 키가 눌린적이 있으면 최하위비트(least significant bit, 0x0001)가 셋된다.
GetAsyncKeyState()는 다른 스레드나 프로세스의 윈도우가 키보드 포커스를 가지고 있어도 값을 리턴해준다.




ps. 스타일러스 스크린이 눌렸을때 VK_LBUTTON 가상키로 감지할 수 있다.
ps2. GetAsyncKeyState가 검출하는 VK_LBUTTON 가상키 상태는 물리적인 마우스의 왼쪽 버튼 상태를 반영한다. 즉, 왼손잡이용으로 좌우 설정을 바꿔놨어도 상관없이 마우스 기계의 왼쪽 버튼 상태를 리턴한다. 참고로 GetSystemMetrics(SM_SWAPBUTTON) 연산을 사용하면 마우스 버튼이 스왑되어 있을 경우 참을 리턴한다.




GetKeyState()와 GetAsyncKeyState()는 아래 가상키를 처리할 수 있다.

VK_CONTROL   /// 좌우 상관없이 처리됨
VK_RCONTROL
VK_LCONTROL
VK_SHIFT
VK_LSHIFT
VK_RSHIFT
VK_MENU
VK_RMENU
VK_LMENU





3. 정리

GetKeyState()는 메시지 큐를 읽어서 리턴하고, GetAsyncKeyState()는 메시지 큐와 비동기적(asynchronized)으로, 즉 메시지 큐와 상관없이 기계입력에 즉각적으로 반응한다.
따라서 게임과 같은 즉각적인 키보드 반응이 필요한 경우 GetAsyncKeyState()가 더 좋다고 할 수 있다. GetKeyState()를 사용하면 만약에 메시지 큐에 메시지가 많이 밀렸을 경우 렉 현상이 생길 수 있기 때문이다.

사실 요즘은 하드웨어 성능이 워낙 빨라서 GetKeyState()를 써도 왠만해선 큰 차이가 느껴지지는 않는다. 그리고 토글키의 상태를 얻어오려면 어차피 반드시 GetKeyState()를 사용해야 한다.



ps. 다른 블로그에서 GetKeyState()는 윈도우 프로시저의 메시지큐 처리 루틴에서만 사용해야 한다는 글이 있었는데 이는 잘못된 정보이다. 영문 해석 과정에서의 실수로 보인다.

2012년 3월 14일 수요일

[C++] const 지정자


아래 두 표현의 차이는 무엇일까?




1.          const int* p;




2.          int* const p; 




스크롤을 내리기 전에 함께 생각해보자 :D












------------- 정답 -------------


1번은 const int 를 가리키는 포인터.
즉, 정수 변수의 값이 변하지 않는다는 뜻이다.

int            a = 5;
const int* p = &a;
*p = 10;           //// 값을 바꾸려 하므로 에러발생




2번은 정수를 가리키는 const 포인터 p이다.
즉, p가 가진 값(주소)가 변하지 않는다는 것을 보장한다.

int            a, b;
int* const p = &a;
p = &b;               //// 저장된 주소를 바꾸려 하므로 에러 발생





아예 둘 다 변하지 않도록 이렇게 써도 된다.

const int* const p;






내 생각에 const를 붙여주는 이유는, 물론 안정성을 높여주는 코딩 습관인 것도 있지만 코드의 가독성을 높이는 것도 큰 이유인 듯 하다.
코드를 읽을때 const가 붙어있으면 아, 이 함수는 이런 일을 하겠구나 라고 이해하기가 더 쉬워지는 것 같다.





※ 클래스에서의 const

예전 포스트에서도 언급했지만,

void ClassName:: FunctionName(void) const;

이처럼 멤버 함수의 선언부 마지막에 const가 붙은 함수를 const 함수라고 하는데,
이것은 컴파일러에게 멤버 변수의 값을 바꾸지 않겠다는 보장을 한 것이다.
예를 들어 어떤 함수에서 인자로 const형 변수를 받아왔을때 내부에서 const 함수 이외의 함수로 처리하려고 하면 에러가 발생한다.
예시는 이전 포스트 참고.

2012년 3월 1일 목요일

[C] 파일 입출력




일단 파일 쓰기 예제



#include <stdio.h>


int main(void)
{
///< 파일 포인터 선언 
FILE* fp = NULL;


int i = 1;
//char c = 'a';
float f = 3.14f;
char pTest[256] = "TEST";
__int64 longlongint = 12345678910;




///< 파일 쓰기 모드로 열기 
fp = fopen("현규바보.txt", "w");


///< 파일 포인터 확인 ( 제대로 열었는지 )
if( fp == NULL )
{
printf("파일 열기 실패\n");
return 0;
}


///< 데이터 쓰기 
fprintf(fp, "%d\t%f\t%s\t%I64d\n", i, f, pTest, longlongint);


///< 파일 포인터 닫기 
fclose(fp);


return 0;
}




fp라는 파일 포인터를 통해 프로그래머가 파일에 접근, 컨트롤하게 된다.

fopen() 에서 파일명만 적어주었는데(파일 이름은 신경쓰지 말자ㅡ,.ㅡ)
이 경우 상대경로로서 작업폴더를 기준으로 파일을 찾는다.
절대 경로를 적어줘도 된다.

그 다음 인자인 "w"는 쓰기 모드로 파일을 연다는 뜻이다.
모드는 아래와 같은 것들이 있다.


rt(또는 r+t)         텍스트 화일 읽기용
wt( " w+t) 쓰기
at( " a+t) 추가붙이기
rb( " r+b) 이진파일 읽기
wb( " w+b) 이진파일 쓰기
ab( " a+b) 이진파일 붙이기



그리고 __int64 자료형은 이름 그대로 64비트 짜리 정수형 타입이다. 기존 long int가 32비트(약 40억) 까지 담을 수 있었던 것에 비해 그보다 훨씬 큰 숫자도 담을 수 있다.
또한 fprintf() 로 쓸 때 이 자료형을 %I64d 로 받아주는 것을 볼 수 있다(대문자 I 이다).

char 형을 일단 주석으로 막아놓은 이유는, 입출력시 char 형을 따로 처리하면 탭문자, 공백문자, 개행문자 등도 하나의 문자로 인식하여 처리가 번잡해지기 때문이다.



결과물 텍스트 파일을 열어서 확인해보고 닫은 뒤

이제는 읽기코드를 실행해보자.





#include <stdio.h>


int main(void)
{
///< 파일 포인터 선언 
FILE* fp = NULL;


int i;
//char c;
float f;
char pTest[256];
__int64 longlongint;




///< 파일 쓰기 모드로 열기 
fp = fopen("현규바보.txt", "r");


///< 파일 포인터 확인 ( 제대로 열었는지 )
if( fp == NULL )
{
printf("파일 열기 실패\n");
return 0;
}


///< 데이터 읽기 
fscanf(fp, "%d%f%s%I64d", &i, &f, pTest, &longlongint);


        ///< 잘 읽었는지 확인
        printf("%I64d", longlongint);
///< 파일 포인터 닫기 
fclose(fp);


return 0;
}





몇가지 눈여겨 볼 점은,
일단 파일을 열 때 읽기 모드로 열었다는 것.
그리고 fscanf() 는 인자를 주소형식으로 받는다는 것..(필자는 C 코딩에 익숙하지 않아서 이런게 아직 신기하다..)
그리고 scanf()는 공백문자, 탭문자, 개행문자 등을 기준으로 자동으로 Tokening을 해준다는 것이다.





마지막으로 아래는 .cpp 파일을 자동으로 생성하는 코드이다.





  FILE* fp = NULL;


  fp = fopen("mainTestFile.cpp", "w");

  ///< 데이터 쓰기 
fprintf(fp, "#include <stdio.h>\n");
fprintf(fp, "int main(void)\n");
fprintf(fp, "{\n");
fprintf(fp, " printf(\"\%%d,1,2\\n\");\n");
fprintf(fp, "}\n");


  ///< 파일 포인터 닫기 
  fclose(fp);





여기서 눈여겨볼 점은 따옴표(")나 역슬레쉬(\) 같은 특수문자를 쓰기 위해서 앞에 역슬레쉬를 붙여줬다는 점.
그리고 % 문자는 특이하게 \%를 앞에 붙여줬다는 점이다.

생성된 cpp 파일을 열어 내용을 확인해 보면 아래와 같다.




#include <stdio.h>
int main(void)
{
printf("%d,1,2\n");
}






2012년 2월 23일 목요일

[C++] 템플릿 (정리중)

템플릿은 헤더에 모든 코드가 들어있어야한다.
즉, 일반적인 파일분할처럼 .h와 .cpp로 정의와 구현이 구분되어 있으면 안된다는것.
그 이유는 컴파일시 inline함수처럼 코드복사가 일어나기 때문이다.


템플릿 코드를 컴파일해서 오브젝트 파일을 메모장으로 열어보면, 실제로는 TestClass<INT> 또는 TestFunc<FLOAT> 과 같은 이름으로 사용되고 있음을 볼 수 있다.




템플릿 함수 특수화 예시 코드

template <typename T>
void TestFunc(T& a)
{
      .....
}

template <>
void TestFunc(int& a)
{
      .....            ////  int 형에 대해서만 특수하게 적용할 코드를 작성한다.
}

[C++] 상속 (정리중)


자식에서 부모 상속시 헤더 파일에서 인클루드 한다. 이유는??


virtual 함수를 선언하면 클래스에 4바이트 공간이 생기고 그것을 상속클래스에서 관리한다.(함수 포인터 공간인듯) 이로 인해 함수 오버라이딩이 가능해진다.


생성자는 부모부터 실행된다. 초기화리스트(이니셜라이저)로 자식에서 부모 생성자를 호출할 수 있다.
소멸자는 자식부터 실행된다. 메모리 누수 등의 문제를 방지하기 위해서. 이 때문에 소멸자는 반드시 virtual 로 선언해야한다.


부모만이 자식을 품을 수 있다.
부모클래스 포인터로 자식객체를 가리킬 수 있다.(업캐스트)
자식클래스 포인터로 부모객체를 가리킬 수 없다.(다운캐스트: 부모클래스 포인터에서 자식포인터로 형변환)
단, 명시적 형변환은 가능.


상태패턴에 사용된다.

[C++][자료구조] 링크드 리스트(Linked List)

개념 같은건 책을 보면 나와있으니 구현시 실질적으로 중요한 것만 정리.


단일이든 양방향이든 리스트의 처음과 끝에 Head와 Tail 노드를 만든다(포인터가 아니라 실제 공간을 할당받은 노드).
이 두 노드는 데이터를 가지지 않고 처음과 끝을 가리키는 역할만 한다.
(구현시에는 리스트 클래스의 생성자와 소멸자에서 두 노드를 할당, 반환한다.)

이렇게 구현하였을때의 장점은 삽입/삭제 등 함수 내부에서 if 문으로 조건 체크를 해줘야할 빈도수가 현저히 줄어들어 구현이 훨씬 간단해 진다는 점.
사용시에도 조건체크를 하지 않으므로 실행 속도가 빠르다는 점.
단점은 두 개의 공간을 낭비한다는 것 뿐이다.


이것이야말로 일반 대학교재용 자료구조책에서는 절대 찾아 볼 수 없는 실용적인 방법론이라 말하지 아니하지 않을 수 없지 아니하지 않을 수 없는것이다ㅡ,.ㅡ






그리고 이건 햄스터의 슬픔(...응??)



[C++] 문자열 함수 外


size_t strlen ( const char * str );

문자열의 실제 길이를 리턴하는 함수이다. 널문자를 제외한 길이라는 점에 주의하자!!! 참고로 size_t 란건 unsigned int를 재정의한 것이다. 






int strcmp ( const char * str1, const char * str2 );

두개의 문자열을 비교하여 동일할 경우 0이 리턴되고, str1이 클 경우 양수, str2가 클 경우 음수가 리턴된다. 여기서 크다는 것은 문자를 아스키 코드값으로 비교한 결과를 말하는 것이다.






errno_t strcpy_s(
   char *strDestination,
   size_t numberOfElements,
   const char *strSource 
);
문자열을 복사하는 strcpy의 새로운 표준형이다. strcpy의 경우 배열의 첨자범위를 넘어설 위험성이 많으므로 최근의 visual studio에서 사용하면 경고 메시지가 뜬다. 중요한건 두번째 인자가 Dest 버퍼의 크기라는 점만 알면 된다. errno_t는 에러체크를 위해 사용되는 리턴값 같은데 실제 사용할때는 신경안써도 된다.


errno_t strcat_s(
   char *strDestination,
   size_t numberOfElements,
   const char *strSource 
);
복사가 아니라 이어붙이기. 나머지 설명은 위와 동일.









아래는 실제로 문자열을 깊은 복사할때의 코드이다.
가장 주의해서 볼 점은 strlen() 함수는 NULL 문자를 제외한 길이를 리턴한다는 점이다.



char szTemp[256];
cin >> szTemp;


int nLen = strlen(szTemp);


char* m_szName = new char[nLen + 1];


strcpy_s(m_szName, nLen + 1, szTemp);








※ 문자열 함수는 아니지만 콘솔 문자열 입력 관련

cin 객체는 한 개의 문자 단위로 입력받아 처리하며, 공백문자나 개행문자(엔터, '\n')로 tokening 된다.
이때 두 개 이상의 문자를 입력하면 그만큼 예외처리가 여러번 일어나는 것을 볼 수 있다.
이것을 막으려면 입력버퍼를 초기화시키는 fflush(stdin); 명령을 사용하면 편리하다.

cin.getline()  공백문자 포함해서 콘솔에서 문장을 입력받아 저장하는데 사용하는 함수





※ 참조:
http://www.cplusplus.com/
http://msdn.microsoft.com

[C++] 연산자 오버로딩


OperatorOverLoad OperatorOverLoad::operator+( const OperatorOverLoad& rhw)
{
OperatorOverLoad temp;
temp.BindXY(m_nX + rhw.m_nX, m_nY + rhw.m_nY );
return    temp;
}



위는 m_nX와 m_nY 두개의 멤버 변수를 가진 클래스에서의 '+' 연산자 오버로딩의 예시이다.

그런데 사실 이건 중요하지 않고ㅡ,.ㅡ;;

복사생성자 관련 포스트에서도 언급한것 같지만 '=' 연산자 오버로딩이 중요하다.





OperatorOverLoad OperatorOverLoad::operator=( const OperatorOverLoad& rhw)
{
///< 자기 자신 확인 
if( this == &rhw )
{
return *this;
}

///< 확인 
SetName(rhw.m_pName);


return *this;
}




void OperatorOverLoad::SetName(const char* pName)
{
///< 포인터 검사 
if( pName != 0 )
{
int nLen = strlen(pName);
if( nLen != 0 )
{
delete [] m_pName;
m_pName = NULL;


m_pName = new char[nLen + 1];
strcpy_s(m_pName,nLen+1,pName);
}
}
}



위 내용을 요약하자면 char* 형의 멤버변수에 대하여 깊은 복사를 해주고 있다.

char* 형의 멤버변수가 존재할 경우 깊은 복사를 하도록 복사생성자와 '=' 연산자 오버로딩을 둘 다 생성해주어야 한다.

[C++][디자인패턴] static과 싱글톤 패턴

디자인 패턴이란

프로그래머들이 프로젝트를 하면서 자주 사용되는 몇가지 유용한 프로그램 스타일 들에 이름을 붙여서 분류한 것이다. 싱글톤, 팩토리, 옵저버, 커맨드, 상태 패턴 등이 있다. 각 패턴들이 어떤 상황에서 사용되는지를 아는 것이 중요하다.



싱글톤 패턴의 특징

-싱글톤은 매니저 역할을 하는 패턴이다.
-단 하나만 존재하는 전역 클래스이다.
-함수와 변수를 static으로 선언하여 사용한다.




<cStudentMgr.h>


#pragma once


class cStudentMgr
{
private:
static cStudentMgr* m_pInstance;
int m_nID;

public:
static cStudentMgr* GetInstance(void);
void Destory(void);


inline void SetID(int nID)
{
m_nID = nID;
}


inline int GetID(void) const
{
return m_nID;
}


private:
cStudentMgr(void);
~cStudentMgr(void);
};





위 코드를 보면 생성자와 소멸자가 private 영역에 들어가 있다.
이런 경우 클래스의 객체를 만들면 생성자와 소멸자가 private이므로 호출할 수 없다는 에러가 발생한다.
생성자와 소멸자 대신에 GetInstance(), Distroy()를 사용해 프로젝트를 통틀어 단 하나의 객체만을 생성한다.
두 함수의 내부 구현은 이렇다.




<cStudentMgr.cpp>



#include "cStudentMgr.h"


///< 정적 초기화 
cStudentMgr* cStudentMgr::m_pInstance = 0;


cStudentMgr::cStudentMgr(void)
:m_nID(0)
{
}


cStudentMgr::~cStudentMgr(void)
{
}


///< 싱글톤 인스턴스 포인터 얻기 
cStudentMgr* cStudentMgr::GetInstance(void)
{
///< 처음 이라면 
if( m_pInstance == 0 )
{
m_pInstance = new cStudentMgr;
}


return m_pInstance;
}


///< 생성된 static 인스턴스 삭제
void cStudentMgr::Destory(void)
{
if( m_pInstance != 0 )
{
delete m_pInstance;
m_pInstance = 0;
}
}






가장 위는 정적변수의 초기화이다.
생성자와 소멸자보다 위의 정적영역에서 초기화되고 있다.

(참고로 생성자에서 강조된 부분은 멤버 이니셜라이저라고 하는데, const 나 레퍼런스 변수처럼 선언과 동시에 초기화되어야 하는 변수들을 클래스에서 초기화해주는 방법이다. 여기서는 굳이 사용할 이유는 없지만 그냥 사용하고 있다-ㅅ-; 또한 상속시에 부모 생성자에 인자를 넘길때도 사용된다.)

그리고 아래 2개의 함수는 단 하나의 객체만을 생성 & 소멸시키는 기능을 하고 있다.
메인함수에서는 아래와 같이 사용된다.




#define ST_MGR cStudentMgr::GetInstance()   /// 사용하기 편리하도록


int main(void)
{
ST_MGR->SetID(1);


cout << ST_MGR->GetID() << endl;

ST_MGR->Destory();


return 0;
}




싱글톤패턴은 프로젝트에 단 하나뿐인 전역 클래스로서 주로 관리자 역할을 하는 클래스를 만들어 사용하는 방식이다.
static 이므로 객체 생성없이 정적 영역의 멤버변수와 함수를 어디서나 호출하여 사용할 수 있다.
특징은 전역변수와 마찬가지로 사용하기엔 편리하지만 남용할 경우 유지보수, 가독성, 재사용성 등에 문제가 발생한다.
따라서 프로젝트당 단 하나의 클래스만 전역으로 만드는 것이 좋으며, 프로젝트 팀장의 성향에 따라 싱글톤을 아예 못쓰게 하는 경우도 있다고 한다.




※ 참고: 정적(static) 함수란?
개념적으로는 정적 멤버변수만을 접근 가능한 함수이다.
클래스가 인스턴스되지 않아도 사용가능하다.
내부적으로는 다른 멤버함수와 달리 this 포인터를 넘기지 않는다.
때문에 c에서 callback으로 넘길때 유용하다(이건 아직 무슨 말인지 모르겠다).
this 포인터가 없으므로 상속해서 오버라이딩 불가능.
또한 c에서는 하나의 파일에서만 해당 함수를 사용하고 싶을때 static을 붙인다.

2012년 2월 20일 월요일

포풍같은 주말을 보내고..

뭐랄까.. 평생 다시 이런 일이 없을지도 모르는 주말이었는데

뭔가 완전히 불태우지 못한것 같아서 아쉽고 또 아쉬움만 남는다.

그래도 감동이었고...

뭔가 응어리같은게 풀린 느낌.. 죽어도 여한이 없다? 까지는 아니지만..ㅎㅎ

콘서트란건 한번은 좌석에서 전체를 보고 한번은 스탠딩을 뛰는것이 진리구나 라고 깨달음ㅋㅋ

아... 주말부터 오늘까지는 아무것도 손에 안잡혔다.

지금 해야할게 많은데...

그냥 에헤라디야 딩가딩가 하면서 살면 안될까요?





2012년 2월 15일 수요일

[C++][클래스] this 포인터

지난 포스트에서 const 인자 관련해서 this 라는 알 수 없는 녀석이 등장했었다.
이번엔 그것을 정리해보겠다.



일단 정의부터,

객체의 멤버함수는 자신을 소유한 객체의 주소를 가리키는 this 라는 포인터를 자동으로 가지게 된다.



예를 들어 아래와 같이 멤버함수를 실행했다고 하자.

int main()
{
    TestClass c1;

    c1.TestFunc();            // *

    return 0;
}


위의 별표로 표시한 라인이 실행될때 자동으로 this 포인터가 생성이 되고 그것이 함수 내부에 인자로 들어간다.

더 자세히 보자면 



TestClass* const this = &c1;             // 가리키는 객체를 바꾸면 안되므로 const
//const TestClass* const this = &c1;    // const 함수의 경우 멤버 변수도 바꾸면 안되므로 또 const



함수 호출시 자동으로 위 선언이 실행되고 
TestFunc() 함수 내부에 this가 인자로서 전달된다.
즉, TestFunc() 내부에서 아래와 같이 this를 사용할 수 있다.



TestFunc()
{
    cout << this->m_ID;
}






※ 보충설명:
컴퓨터 내부적으로는 각 객체에 대하여 멤버변수는 각각 메모리를 할당받지만 멤버함수는 그렇게 할 필요가 없다. 어차피 같은 내용이므로 메모리 낭비가 되기 때문이다. 그렇기 때문에 실제 메모리상에 멤버함수의 코드부분을 저장하는 곳은 클래스당 한 곳만 존재하면 되지만, 대신에 각 멤버함수를 객체별로 구분하기 위하여 this 포인터의 개념이 도입된 것이다. [참조: 뇌를 자극하는 C++, p.622]





[C++][클래스] 복사 생성자

복사 생성자의 형태는 아래와 같다.


클래스명(const  클래스명&  name)
{
         /// 내용
}



- 인자가 레퍼런스인 이유는?
인자를 직접 복사받아서 사용한다면 복사생성자 내에서 다른 복사생성자를 호출하고 그 복사 생성자가 다시 다른 복사생성자를 호출하는 무한 루프 상황이 발생할 수 있으며, 그때 실제 메모리 복사가 일어나므로 메모리 오버플로우 등의 에러가 발생할 수 있기 때문이다.
레퍼런스의 경우 메모리에 실체가 복사되는 것이 아니므로 안전하다.



- 복사 생성자를 정의하는 이유?
클래스에 char* 처럼 메모리 동적할당을 받는 멤버변수가 있을 경우, 반드시 복사 생성자를 직접 정의하여 '깊은 복사'를 해줘야한다.

이때 복사생성자와 쌍으로 대입연산자(=)를 오버로딩하여 마찬가지로 깊은 복사를 하도록 정의해 주어야 한다.

참고로 깊은 복사란 메모리 공간을 별도로 할당 받고 그 내용을 복사받는 형태, 얕은 복사는 포인터가 가리키는 주소값 자체를 복사해 오는 것이다.
디폴트 복사 생성자는 얕은 복사를 하는데, 이 경우 같은 주소를 여러개의 다른 객체가 가리키는 것이므로 각각의 객체가 소멸될때마다 delete 명령어로 같은 메모리를 여러번 삭제하는 위험한 사태가 발생할 수 있게 된다.



- 복사 생성자가 호출되는 3가지 경우

1. 객체를 선언하고 초기화 할때

cTestClass tc1;
cTestClass tc2 = tc1;         // 여기서 호출된다.


2. 함수 인자로 객체가 복사되어 들어갈때(임시객체 생성)

void   TestFunc(TestClass tc);


3. 함수에서 객체를 값으로 복사해서 리턴할때(임시객체 생성)

TestClass  TestFunc();











[C++][클래스] 객체 레퍼런스를 인자로 받을때 const 사용시 에러발생 문제


// 다른 객체에서 정보 복사해오기
//void cStudentInfo::CopyInfo(const cStudentInfo& c)
void cStudentInfo::CopyInfo(cStudentInfo& c)
{
char* newID = c.GetID();
char* newName = c.GetName();
int newKorPnt = c.GetKorPoint();
int newEngPnt = c.GetEngPoint();
int newMatPnt = c.GetMatPoint();


SetID(newID);
SetName(newName);
SetKorPoint(newKorPnt);
SetEngPoint(newEngPnt);
SetMatPoint(newMatPnt);
}



위의 코드는 클래스 멤버함수의 정의부이다.
자신과 동일한 클래스 타입 객체 레퍼런스를 인자로 받아 내용을 복사하는 함수인데
인자에 const를 붙이면 Getxxx() 함수를 사용할때 아래와 같은 에러가 난다.



1>c:\documents and settings\baikanp\my documents\visual studio 2008\projects\120214\report.cpp(146) : error C2662: 'cStudentInfo::GetID' : 'this' 포인터를 'const cStudentInfo'에서 'cStudentInfo &'(으)로 변환할 수 없습니다.



const를 제거하면 정상적으로 빌드가 되며,
참고로 Getxxx() 함수는 단순히 멤버변수를 리턴하는 함수이다.








남쌤에게 문의한 결과,
const로 선언하면 cStudentInfo& c 가 절대 변하지 않는다는 보장을 해준 것인데, 
c.Getxxx() 함수를 사용할 때 임시객체의 생성과 소멸 등이 발생하여 객체에 변경이 생기기 때문에 에러가 나는 것이라 한다.
지금으로서는 확실히 이해가 안되지만 Getxxx() 함수를 사용하는 것이 객체에 변경을 가져온다는 것으로 이해면 될것같다.
아마도 에러메시지로 보건데, 객체에 암묵적으로 포함된 this 라는 포인터가 변경되는 것이 직접적인 에러의 원인인듯 하다.




해결법은 두가지이다. 
가장 위의 코드처럼 const를 제거하거나, 
또는 아래와 같이 Getxxx() 함수를 거치지 않고 직접 멤버변수를 복사하는 것이다.



void cStudentInfo::CopyInfo(const cStudentInfo& c)
{
char* newID = c.m_szID;
char* newName = c.m_szName;
int newKorPnt = c.m_nKorPoint;
int newEngPnt = c.m_nEngPoint;
int newMatPnt = c.m_nMatPoint;






여기서 또 한가지 재밌는 것은 위 멤버 변수들은 private 로 선언되어 있다는 것이다.
즉,  어떤 객체의 메소드는 같은 타입 다른 객체를 인자로 받았을 때 그  private 멤버변수를 자유롭게 접근할 수 있다는 것이다.
(예를 들어 복사 생성자의 경우도 함수 내부에서 타 객체의 멤버를 직접 접근하고 있다.)








※ 내용보충 (2012. 02. 16):
const 객체를 사용해서 const가 아닌 멤버함수를 호출하는 것은 불가능하다. 컴퓨터는 const가 아닌 멤버함수는 멤버변수의 값을 변경한다고 생각하기 때문이다(뇌를 자극하는 C++ p.599). 위 문제의 경우 Get 함수들을 아래와 같이 const 함수로 선언해주면 const 객체를 통해서 사용해도 에러가 생기지 않는다. 참고로 const 함수란 멤버변수의 값을 변경하지 않는 함수를 말한다.




char* cStudentInfo::GetID(void) const