전역변수로 사용하던

float m_vStartPoint3D[3] = {0.0f, 0.0f, 0.0f};

float m_vEndPoint3D[3] = {0.0f, 0.0f, 0.0f};

 

이런 형태를

Class 하여

 

{

public:

float m_vStartPoint3D[3];

float m_vEndPoint3D[3];

.

.

.

}

 

생성자

{

m_vStartPoint3D[3] = {0.0f, 0.0f, 0.0f};

m_vEndPoint3D[3] = {0.0f, 0.0f, 0.0f};

.

.

.

}

형태로 바꾸었다.그런데 Error 발생

 

1>.\SourceFiles\AAA.cpp(18) : error C2059: syntax error : '{'

1>.\SourceFiles\AAA.cpp(18) : error C2143: syntax error : missing ';' before '{'

1>.\SourceFiles\AAA.cpp(18) : error C2143: syntax error : missing ';' before '}'

1>.\SourceFiles\AAA.cpp(19) : error C2059: syntax error : '{'

1>.\SourceFiles\AAA.cpp(19) : error C2143: syntax error : missing ';' before '{'

1>.\SourceFiles\AAA.cpp(19) : error C2143: syntax error : missing ';' before '}'

 

해결방법은 {0.0f, 0.0f, 0.0f} -> (0.0f, 0.0f, 0.0f) 형태로 바꿔준다.

'Programming > Visual Studio' 카테고리의 다른 글

error LNK2005:  (0) 2010.01.13
error LNK1104  (1) 2010.01.13
error LNK2019 fatal, error LNK1120  (0) 2010.01.13
VisualStudio 메모리 누수 체크 사용하기  (0) 2009.12.19
error LNK2005: 에러  (0) 2009.12.19
Posted by 시그v

 

LINK : fatal error LNK1104: cannot open file '(...생략...).intermediate.manifest'

< error 발생 원인 >

dll파일이 생성 되는 위치를 변경 발생 하였다.

해결방법은 dll파일이 있는 경로로 바꿔 주면 된다.


 < 해결 방법 >

  1. dll파일경로를  ./bin/경로로 변경 하였다고 한다면
    Project -> Property -> Linker -> Manifest File -> Manifest File 에서

(기존경로).intermediate.manifest -> ./bin/(기존경로).intermediate.manifest 변경하면 된다.

 

  1. manifest파일을 생성하지 않게 하는 방법

Project -> Property -> Linker -> Manifest File -> Generate Manifest

Yes에서 No 변경한다.

'Programming > Visual Studio' 카테고리의 다른 글

error LNK2005:  (0) 2010.01.13
error LNK2059  (0) 2010.01.13
error LNK2019 fatal, error LNK1120  (0) 2010.01.13
VisualStudio 메모리 누수 체크 사용하기  (0) 2009.12.19
error LNK2005: 에러  (0) 2009.12.19
Posted by 시그v

 

참고  <http://edible.egloos.com/1240411>
LNK2019, LNK1120 Error 발생이유...

1. 헤더를 include 하고 프로젝트에 헤더 .cpp 포함하지 않을 경우...

2. .h .cpp 함수원형 함수호출을 해놓고 정작 함수 구현을 했을 경우...

3. .c .cpp 프로젝트에서 같이 사용할 .h include하고 .cpp include 하지 않을 경우...


 ===============================

-aaa.h-

void CheckLNK2019(int ntest);

-aaa.cpp-

void LNK2019Test() {

CheckLNK2019(nTemp);

}

===============================



< 내가 실수한 부분 >
-test.h-

class test

{

void CheckLNK2019(int ntest);

};

-test.cpp-

void test::LNK2019Test() {       test:: =>이걸 빼먹었다;;;

            (생략)

}

'Programming > Visual Studio' 카테고리의 다른 글

error LNK2005:  (0) 2010.01.13
error LNK2059  (0) 2010.01.13
error LNK1104  (1) 2010.01.13
VisualStudio 메모리 누수 체크 사용하기  (0) 2009.12.19
error LNK2005: 에러  (0) 2009.12.19
Posted by 시그v

'Programming' 카테고리의 다른 글

현재 시간 얻기  (0) 2010.01.14
코드 실행 시간 측정  (0) 2010.01.14
C언어->WinAPI->DirectX->C++->STL->Socket->MFC->Direct3D또는OpenGL  (0) 2009.12.23
Posted by 시그v

추천하는 순서는 다음과 같습니다.

 

C언어->WinAPI->DirectX->C++->STL->Socket->MFC->Direct3D또는OpenGL.

여기까지 배우시면 왠만한 온라인 게임 정도는 만들 수 있습니다.

게임회사에 취직하시려면 Direct3D쪽을 강력히 권해드립니다.

 
라이브러리 프로그래머라...??;;;;

자신의 라이브러리를 높은 래밸로 창조하고 싶으시면,

C++공부를 많이 하고 경험을 많이 쌓으시길 바랍니다.

요즘은 C언어보다 C++언어를 더 먼저 배울만큼 객체지향을 중요시 하는 시대라서,

무엇보다 OOP설계능력을 키우는게 급선무입니다.

그러려면 대형프로젝트에 자주 참가하셔서 많이 만드셔야 하지요.

하지만 대형 프로젝트에 참가하려면 C언어 기초쯤은 완벽히 마스터 해야합니다.

'Programming' 카테고리의 다른 글

현재 시간 얻기  (0) 2010.01.14
코드 실행 시간 측정  (0) 2010.01.14
Fundamentals of Physics with Ramamurti Shankar  (0) 2009.12.25
Posted by 시그v

MFC를 사용하여 프로그램을 개발하고 있다면,

#ifdef _DEBUG
#define new
DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif


위의 코드를 .cpp 화일에 넣어 프로그램이 종료 되었을때 아래와 같이 누수된 메모리를 할당하는 부분의 소스 코드와 라인 수를 출력 해준다.

Detected memory leaks!
Dumping objects ->
d:\sample\sample.cpp(35) : {48} client block at 0x003739D0, subtype 0, 4 bytes long.
 Data: <(   > 28 00 00 00
d:\sample\sample.cpp(34) : {47} client block at 0x00373990, subtype 0, 4 bytes long.
 Data: <    > 1E 00 00 00
Object dump complete.


위의 예에서는 두 블록의 메모리 누수가 검출 되었는데 각 라인의 의미는

 

 



d:\sample\sample.cp (35) : {48} client block at 0x003739D0, subtype 0, 4 bytes long.


sample.cpp 화일의 35번째 라인에서 할당된 메모리가 누수되었고 그것은 48번째로 할당된 메모리이며 메모리 주소는 0x003739d0, 4Byte가 누수되었다는 의미로 해석할 수 있다.

MFC를 사용하지 않은 프로그램의 경우 위의 DEFINE 문으로는 DEBUG_NEW를 찾을 수 없다는 컴파일 에러를 내뱉는데 그 경우엔 위의 DEFINE 문과 함께 아래와 같은 라인을 헤더에 삽입해서 해결할 수 있다.

#if !defined(_AFXDLL)
   #include <windows.h>
   #include <crtdbg.h>
   #if defined(DEBUG) | defined(_DEBUG)
      #if !defined(DEBUG_NEW)
         #define DEBUG_NEW new(_CLIENT_BLOCK, __FILE__, __LINE__)
      #endif
   #endif
#endif


DEBUG_NEW를 새로이 정의함으로써 DEBUG_NEW를 찾을 수 없는 문제를 해결한다.

간혹 malloc 을 이용한 메모리 할당은 위의 내용이 적용이 되질 않는데 이 경우엔 아래의 문장을 소스코드에 삽입한다.

- 헤더 화일에는 아래라인을 추가
#define DEBUG_MALLOC(size) _malloc_dbg(size, _NORMAL_BLOCK, __FILE__ , __LINE__)

- malloc을 재정의 하려는 소스 코드에 아래 라인을 추가.
#define malloc DEBUG_MALLOC


위의 방법들로 memory leak dump 에서 할당된 소스 코드와 라인수를  바로 찾을 수 있다.

 

 

[펌] 개발자의 끄적임 http://cromit.tistory.com/6

'Programming > Visual Studio' 카테고리의 다른 글

error LNK2005:  (0) 2010.01.13
error LNK2059  (0) 2010.01.13
error LNK1104  (1) 2010.01.13
error LNK2019 fatal, error LNK1120  (0) 2010.01.13
error LNK2005: 에러  (0) 2009.12.19
Posted by 시그v

[출처]착하게...삽시다! | 우기우기  http://blog.naver.com/ragcarib/12005303968


프로그램이 메모리에 올라가게 되면 Data Segment 와 Code Segment 로 나뉘게 된다.

 

Heap 과 Stack 은 Data Segment 를 이용하게 된다.

 

  • 힙(Heap)은 런 타임시에 크기가 결정되는 요소들이 저장되는 공간이다.

C의 malloc() 함수나 C++의 new 연산자로 메모리 할당이 될 때에는 이 Heap 공간에 메모리가 잡히게 된다.

 

  • 스택(Stack)은 컴파일시에 크기가 결정되어있는 요소들이 저장되는 공간이다.

함수가 받는 매개 변수나 함수내에서 사용되는 지역변수가 이 Stack 영역에 저장이 된다.

 

 

메모리를 가상으로 그림으로 그려보면 다음과 같이 나타낼 수 있다.

 

 

※데이터 영역

전역 변수와 static 변수 저장

----------------------------------

※힙 영역

동적 할당되는 데이터 저장

 (데이터가 위부터 순차적으로 저장)

 

 

 

※스택 영역

지역 변수와 매개변수가 저장

 (데이터가 아래부터 순차적으로 저장)

 

 

보통 전역 변수나 static 변수는 heap 의 윗 부분에 위치하고, Stack 은 Data Segment 의 처음부터 할당이 되고,

 

Heap 은 끝부분 부터 할당이 된다.

 

 

예제 코드를 작성하여 돌려보았다.

 

 

#include <stdio.h>

int A, B;

main()
{
 int a = 0;
 int b = 0;

 int *p1 = NULL;
 int *p2 = NULL;

 p1 = (int*)malloc(sizeof(A));
 p2 = (int*)malloc(sizeof(A));

 printf("전역 변수의 주소값 출력\n");
 printf("%d\n", &A);
 printf("%d\n", &B);
 printf("동적할당된 포인터의 주소값 출력\n");
 printf("%d\n", p1);
 printf("%d\n", p2);
 printf("지역 변수의 주소값 출력\n");
 printf("%d\n", &a);
 printf("%d\n", &b);

 free(p1);
 free(p2);
}

 

 

 

Visual Studio 2003에서 작성하였고, 알아보기 쉽게 10진수로 출력을 하였다.

 

메모리 할당이 된 간격은 틀리지만,

 

전역 변수와 동적 할당된 포인터의 메모리 할당 순서가 지역 변수의 메모리 할당 순서와 다른것을 확인할 수 있다.

 

 

Stack 영역에 올라가는 데이터는 프로그램이 실행되자마자 마로 메모리에 할당이 되고, 함수가 종료되거나, 프로그램이 종료될 때 자동으로 메모리 공간이 해제된다.

 

 

그에 반해, Heap 영역에 올라가는 데이터는 프로그램이 실행되는 중간에 메모리에 할당되고, 프로그램이 종료될 때

 

자동으로 메모리 공간이 해제되지 않는다. 메모리 공간을 해제하면 다시 그 영역을 사용할 수 있다.

 

 

따라서 동적으로 할당된 메모리 공간은 C의 free() 함수나 C++의 delete 연산자를 사용하여 메모리 공간을 다시 해제하여 주어야 한다.

 

그렇지 않을 경우 메모리 누수(leak) 가 발생하게 된다.

 

 

그렇다면 메모리 동적할당이 왜 필요한 것인가?

 

 

예를들어 다음과 같이 5개의 정수형 데이터를 입력하는 프로그램이 있다고 하자.

 

#include <stdio.h>

 

main()
{
 int arr[5];

 int i = 0;
 for(i = 0; i < 5; i++) {
  printf("정수 입력 : ");
  scanf("%d", &arr[i]);
 }
 for(i = 0; i < 5; i++) {
  printf("%d번째 입력 데이터 : %d\n", i+1, arr[i]);
 }
}

 

이 프로그램은 죽었다 깨어나도 5개의 정수형 데이터 밖에 입력을 하지 못한다. 이제 이 프로그램을 실행 시 마다 입력 되는 데이터의

 

수가 다르게 작성을 하고 싶어서 다음과 같이 프로그램을 작성하였다.

 

#include <stdio.h>

void func(int n);

main()
{
 int *p = NULL;
 int i = 0, n = 0;
 printf("입력할 데이터의 수를 입력 : ");
 scanf("%d", &n);
 func(n);
}

void func(int n)
{
 int arr[n];                     // Compile Error
 int i = 0;
 for(i = 0; i < n; i++){
  printf("정수 입력 : ");
  scanf("%d", &arr[i]);
 }
 for(i = 0; i < n; i++)
  printf("%d번째 입력 데이터 : %d\n", i+1, arr[i]);
}

 

-------------------------------------------------------------------------------------------------------------------

func() 함수 내의 arr 이라는 배열은 func 함수가 실행된 후 종료될 때 스택 메모리 영역에서 메모리가 해제 되므로

 

출력까지 func 함수 내에서 처리하였다. 주의깊게 보아야 할 부분은 배열 선언 시 인덱스 값으로 변수가 올 수 없다는 것이다.

 

(배열 선언시의 인덱스 값에는 상수만이 올 수 있다.)

-------------------------------------------------------------------------------------------------------------------

 

그럼 다음과 같은 문제를 포인터를 사용한 동적 메모리 할당으로 해결해보면 다음과 같은 방식으로 작성할 수 있다.

 

포인터와 배열의 차이점과 공통점을 어느정도 알고 있어야 한다.

 

#include <stdio.h>
#include <stdlib.h>


main()
{
 int *p = NULL;
 int i = 0, n = 0;
 printf("입력할 데이터의 수를 입력 : ");
 scanf("%d", &n);
 p = (int*)malloc(sizeof(int)*n);
 for(i = 0; i < n; i++){
  printf("정수 입력 : ");
  scanf("%d", (p)+i);
 }
 for(i = 0; i < n; i++)
  printf("%d번 입력 데이터 : %d\n", i+1, *((p)+i));

 free(p);                   // Important!!
}

 

 

-------------------------------------------------------------------------------------------------------------------

main() 함수 내에 포인터 변수 p가 선언되어 있고, 이를 입력받은 n의 갯수만큼 int 형으로 동적 메모리 할당을 하고 있다.

이후 포인터 변수 p를 이용하여 데이터를 입력받고 출력 한 후 할당받은 메모리 공간을 해제한다.

-------------------------------------------------------------------------------------------------------------------

 

 

동적 메모리 할당을 사용하면 위와 같은 이점을 활용할 수 있게된다.

 

 

C++에서의 new 와 delete 연산자를 이용한 메모리 할당/해제 방식은

 

 

int *p;

p = new int[데이터갯수];

delete[] p;

 

와 같은 방법으로 할당과 해제를 한다.

 

예를들어 '데이터 갯수' 부분에 5를 입력하게 되면

 

malloc(sizeof(int)*5) 와 같은 크기를 할당하게 된다. (new 연산자 사용시에는 데이터 타입을 알 수 있다.)

 


Posted by 시그v

기본 개념 작성일 : 1997 (In Skyteam)

작성일 : 2002년 11월 13 (In SkySoft)

1개정일 : 2005년 4월 13 (In Freechal)

작성자 : 채경석 kyuseo99@chol.com http://p8.co.kr


개요

본 가이드는 절대적이거나, 표준으로 규정된 내용들이 아니라 하나의 지침사항입니다.

프로그래밍에 조금의 지식이 있는 프로그래머라면 ‘(‘ 뒤에 공백이 있던지 말던지 ‘{‘ 어떻게 붙이던지 코드를 분석하는데 별다른 어려움이 없습니다. 하지만 본 가이드를 힘들게 작성하고 배우고 지켜야 하는 이유는, 좀 더 코드를 분석하는 시간을 줄이고 이해하기 쉽도록 하기 위해서 입니다. 이것이 적어도 동일한 프로젝트에서, 적어도 동일한 팀에서, 같은 스타일로 제작해야 하는 이유입니다.

아래 사항들은 표준도 아니고 절대적이지도 않기 때문에 일부분을 자신들의 팀이나, 프로젝트 스타일에 바꾸어 수정해서 사용하셔도 무방합니다.

 

 

 

1          이름

1.1          변수의 표기는 헝가리안 표기법을 사용한다.
(
헝가리안 표기법이 타이핑하기 불편하거나 귀찮을 수도 있으나 근본적으로 변수의 명확성을 강화한다.)
)     int nNumber, char szName, CString strInfo, CSurface surSportsCar, int *pnData, CWnd wndMain

1.2          각 변수 및 함수의 이름은 대문자로 시작한다.
(
소문자만을 이용하거나 ‘_’을 이용하거나 또는 소문자 + 첫 단어 대문자를 이용하는 많은 사용 예가 있기는 하지만 결국 여러 스타일을 동일 프로젝트에서 혼용하여 사용하는 것은 통일성과 가독성 등에 저해를 가져온다. 때문에 다양한 방법 중 한가지를 선택할 필요성을 느꼈고, 첫 문자를 대문자로 쓰는 것으로 통일하였다.)
)      int nTypeMax, char szFileInfomation, (O)
          int ntype_max, char szfile_infomation (X)
          int GetType();   BOOL SetMaxRange( int CX, int CY ), void SetFont( HFONT hFont )

1.3          헝가리안 표기법 중 포인터는 중첩하여 사용 가능하다.
(포인터 뿐만이
아니라 배열의 ‘a’ 나 다른 의미의 명확성을 위해서 몇몇 헝가리안 표기법도 중첩사용이 가능하다.)
)      int *pnCount, CSurface *psurBack, BYTE *pBuffer

1.4          헝가리안 표기법의 예외는 다음과 같다.
(
헝가리안 표기법이 만능도 아니고 절대적인 것도 아니다. 단순히 습관적이고 일반화된 선례 등은 예외가 될 수 있다.)

1.4.1          X, Y 와 같은 의미가 명확한 변수 ( 확장 : XDest, YDest )

1.4.2          For 문 등에서 사용하는 I, j 등의 이미 알려진 변수

1.4.3          구조체 멤버 변수

1.4.4          BYTE *pBuffer 와 같이 BYTE 라는 값이 큰 의미가 없는 경우

1.5          전역 변수는 ‘g_’ 을 사용한다.
(
지역변수 및 인자와 구분을 위해서 ‘g_’을 사용한다. 참고로 전역 변수는 가능한 사용을 자제한다.)
)     int g_nGameTimer, char g_szAppName

1.6          클래스의 멤버 변수는 ‘m_’ 을 사용한다.
(
지역변수 및 인자와 구분을 위해서 ‘m_’을 사용한다.)
)     m_surBack, m_szName, m_dwCode

1.7          포인터(*) 및 참조(&) 표기는 변수 앞에 붙여서 선언한다.
(
변수 앞에 붙여서 표기 하는 가장 중요한 이유는 char* szName, szType 과 같이 표기 할 경우 szType 이 포인터인지 아닌지 구분이 잘되지 않기 때문이다.)
올바른 예)
POINT *pPoint, char *szName;
POINT &pPoint, char &szName;

잘못된 예)
POINT* pPoint, char * szName;
POINT& pPoint, char & szName;

1.8          #define enum const 는 대문자와 단어 사이에 ‘_’ 붙인다.
)      #define WM_USER_PING_THREAD_END WM_USER+0x102
          #define PACKET_LOGIN_ANSWER_BAD_USER_OVER            8
          enum BUTTON_STATE{ BUTTON_NORMAL, BUTTON_OVER, BUTTON_DOWN, BUTTON_DISABLE };

1.9          #define 보다 const 형 또는 static const 형을 되도록 사용한다. (#define 과 같은 대문자 선언 시에 사용)
(
하지만 배열 등에 직접적으로 const 형은 컴파일 오류가 발생하기 때문에 현재 보류한다.)
) static const int MAX_CARD = 1000

1.10       클래스 내부에서 사용하는 상수 값은 클래스 내부에서 선언된 enum을 사용한다.
(#define
은 전역적으로 사용된다. 따라서 MAX_ITEM과 같은 일반적인 상수이름은 다른 곳에서 겹칠 우려가 있기 때문에 사용하기가 꺼려진다. 하지만 enum은 클래스 내부에서만 사용되기 때문에 MAX_ITEM와 같은 일반화된 상수를 사용할 수가 있다.)

1.11       구조체 이름은 대문자로 하고 각 단어 구분은 ‘_’ 로 한다.
(
구조체의 이름은 관습적으로 대문자만을 사용한다.)
)     struct TEST{ … }
          struct BANNER_UNIT{ … }, struct GAME_ITEM{ … },


1.12       구조체의 멤버 변수는 소문자를 이용하고 단어 별 구분 시에 ‘_’ 을 사용한다.
(
헝가리안 표기법이 사용되지 않는 이유는 오랜 기간 관습적으로 소문자 표기법이 이용되었기 때문이다.)
)
struct GAME_UNIT
{
          BYTE type;
          char id[13];
          WORD version;
          BYTE* buffer;
          CString ip_name,
};

1.13       일반적으로 지역 변수는 선언된 타입과 동일한 이름을 사용한다.
(
클래스나 구조체등의 이름을 이용하여 지역 변수를 선언하면 알아보기가 쉽고 변수명 작명을 위해 고민하지 않아도 된다.)
)     BANNER_UNIT BannerUnit, FAST_TABLE FastTable, CSkyPassword SkyPassword

1.14       템플릿 타입은 대문자 한 자로 표기한다.
(
관습에 의하여 템플릿은 한 자로 표기한다.)
)     template< class T>, template< class C, class D >

1.15       클래스를 이름은 첫 자를 C 시작한다.
(
클래스를 구분하는 헝가리안 표기법의 일종이다.)
)     CSurface, CPoint

1.16       클래스 함수의 경우 클래스 명에서 이미 명시된 부분은 제외한다.
(
이미 클래스 이름으로도 충분히 알 수 있는 내용을 중복하여 적는 것은 비효율적이며 올바르지 않는 방법이다. 이것은 마치 사각형의 사각형 가로크기는 얼마인가요?” 와 같은 표현이다.)
올바른 예)       Rect.GetWidth()
잘못된 예)      Rect.GetRectWidth()

1.17       BOOL형 변수는 사용하지 않는다.
(
빠른 이해와 잘못된 사용을 방지하기 위해서 BOOL은 항상 인 값을 사용한다. 이는 ‘!’ 와 같은 구문을 이용할 때 더욱더 편리하다.)
올바른 예)       BOOL bError 
잘못된 예)      BOOL bNoError

1.18       Enum의 경우 열거형으로 사용시 변수명의 앞에 표기한다. (단 클래스 내부에서 #define의 대용으로 사용시에는 예외이다.)
(enum
열거형으로 사용시 타입을 항상 표기함으로써 값들이 사용시 어떠한 열거형 인지 구분을 쉽게 해준다.)
)    
enum COLOR
{
          COLOR_RED,
         COLOR_GREEN,
         COLOR_BLUE,
};

1.19       함수명의 경우 동사가 앞에 오고 명사가 뒤에 오도록 한다.
(
영문법에 맞는 규칙으로 통일되게 사용한다.)
)      GetLine(), PlaySound(), MoveWindow()

1.20       BMP MP3, ID, XML등의 약어의 표현은 첫 대문자로 한다.
)     CBmpFile, m_Mp3, strUserId, CXml

 

2          일반

2.1          bool, true, false 는 사용하지 않고 BOOL, TRUE, FALSE 사용한다.
(BOOL
등은 4바이트 자료형이고 bool등은 1비트(실제로는 1바이트) 자료형이다. 32비트 운영체계의 경우 속도와 비교하는데 걸리는 성능 부분에도 유리하고 알아보기도 좋기 때문에 위와 같은 대문자로 된 자료형을 사용한다.)

2.2          한 라인이 너무 길지 않도록 주의한다.
(
정확히 컬럼의 크기가 정해진 것은 아니지만 일반적인 프로젝트 팀에서 사용하는 일반적인 해상도(1024~1280)에서 한눈에 보기 좋도록 작성한다.)

2.3          성능 향상 및 가독성 증가를 위해서 본 가이드라인은 위배 될 수 있다. (! 확고한 근거가 존재해야 한다)
(
가이드라인의 목표는 보기 좋고 통일된 코드작성이 가장 중요한 목표이다. 특수한 경우 가독성이나 성능 향상을 위해서 더 좋은 방법이 있다. 그러한 경우에는 본 가이드라인을 따르지 않는다.)

2.4          변수의 형 변환은 (자료형) 과 같은 변환을 사용한다.
(
일반적으로 여러 서적과 C++ 권고안에서 static_cast, const_cast등의 cast를 이용한 형 변환을 권고하고 있으나 타이핑의 불편, 가독성의 감소, 다양한 cast의 적절한 사용 등으로 인한 이유로 사용하는데 어려움이 있다.)
)
int nSize = ( int ) byLength; (O)
int nSize = const_cast<int> byLength; (X)

2.5          함수의 인자는 NULL 입력이 가능하다면 포인터(*)를 이용하고 그렇지 않다면 참조(&)을 사용한다.
(
포인터 방식의 최대 단점은 NULL을 원하지 않은 경우에도 NULL이 입력될 수 있다는 점이다. 따라서 이러한 것을 근본적으로 막기 위해서는 NULL이 입력되지 않기를 원하는 함수의 인자는 참조를 사용한다.)
)     int Draw( CSurface &rSurface );                              (NULL이 입력 불가한 경우)
          void GetValue(int *pId, int *pAge, int *pDate = NULL );          (NULL
이 입력 가능한 경우)

2.6          전역함수, API함수, 표준 C++ 라이블러리의 경우 일반적으로 ‘::’ 을 가능한 사용하지 않는다. (멤버 함수와 동일한 이름인 경우에만 ‘::’ 을 사용한다.)
(
이와는 별개로 전역 함수의 사용은 최소한 한다.)
올바른 예)       int nCount = ::GetWindowCount();               int nSize = strlen( szName );
잘못된 예)       int nCount = GetWindowCount();                 int nSize = ::strlen( szName );

2.7          STL 사용시 std:: 는 사용하지 않는다.
(
프로젝트에서 STL을 표준으로 사용하기를 결정했다면 ‘using std;’을 사용한다.)
올바른 예)        std::list List
잘못된 예)      list List


2.8          지역변수는 필요한 시점에서 선언한다.
(C++
에서는 변수를 미리 선언할 필요성이 없다. 단 기능이나 사용이 비슷한 변수들이 선언된다면 가독성 증가를 위해서 미리 선언 될 수 있다.)

2.9          매직 넘버(Magic Number)는 가능한 사용하지 않는다.
가능한 #define 이나 enum, static const 등을 이용하여 매직 넘버의 사용을 최소화한다.
(
개수, 최대값, 최소값, 범위 등의 매직 넘버는 사용을 최대한 자제하나 오히려 가독성과 사용용도가 떨어지는 이미지의 좌표값과 같은 일시적인 것은 특별히 사용을 회피 할 필요는 없다. 또한 0, 1, -1의 경우는 매직 넘버에서 예외가 될 수 있다.)

2.10       goto의 사용을 자제한다.
(goto
의 사용은 프로그램의 구조를 파악하기 어렵게 한다. 하지만 매우 특수한 경우에는 사용 될 수 있다.)

2.11       전역 변수나 멤버 변수보다는 지역변수를 사용한다.
(
전역 변수와 멤버 변수는 매우 오랜 기간 동안 생존해있고 중요한 변수이기 때문에 작성자를 포함한 프로그래머들은 그 변수들을 분석하고 기억하려고 한다. 따라서 사용을 최소화 한다면 그러한 수고가 줄어든다.)

2.12       포인터, 핸들은 NULL과 비교하고 숫자는 0과 비교한다.

2.13       복잡한 구문은 임시 변수를 사용한다.
(
부적절한 3항 연산자나 각종 비교구문의 중첩을 이용한 복잡한 코드는 가독성을 저해한다.)

올바른 예)
BOOL bCompare1 = ( nCount > 100 && nSize < 50 );
BOOL bCompare2 = ( nCount == 50 || nSize < 20 );
if( bCompare1 || bCompare2 )…      

잘못된 예)
if( ( nCount > 100 && nSize < 50 ) || ( nCount == 50 || nSize < 20 ) )…


3          클래스

3.1          클레스의 기본 구조는 다음과 같다.
(
생성자, 공개함수, 비공개함수, 멤버변수의 순서로 배열하고 각각은 public, protected 등으로 추가로 명기하여 구분한다. )

)
class CSound
{
public:                                 : public
함수를 최 상단에 배치한다.
    CSound();                     :
생성자를 최 상단에 배치한다.
    virtual ~CSound();               :
종결자는 일반적으로 virtual 이다.

    void Play();                          :
멤버 함수
    void Stop();
                                        : public, protected
등은 1라인 비운다.
protected:                                  : protected
함수를 배치한다.
    void PlayWav();                  
    void PlayOgg();
                                                 :
각각의 함수별 관련성 및 블록이 있다면 1라인 비운다.
    void StopWav();
    void StopOgg();

protected:                                 :
멤버 변수를 배치한다.  
    char m_szFileName[256];
    int m_nSize;
};

3.2          모든 멤버 변수는 public으로 선언하지 않는다.
(
정보은닉과 캡슐화를 위해서 멤버 변수들은 public으로 선언하지 않는다.)

3.3          inline 함수의 경우 헤더 선언에 inline 을 표기하지 않는다.
(
헤더 파일에 inline으로 명기하여 선언을 하는 것과 별개로 실제 구현에 inline으로 작성하면 함수는 inline으로 작동된다. 클래스 헤더를 잘 알아볼 수 없게 하는 inline은 굳이 사용 할 필요성이 없다.)
)
class CSound
{
올바른 예)       BOOL Play();
잘못된 예)       inline BOOL Play();         
};


3.4          inline 함수의 경우 선언구문 1라인 아래 줄에 입력한다.
(inline
함수는 헤더 파일의 하단에 선언하고 너무 많은 inline 함수가 존재한다면 *.ini 파일을 작성하여 헤더에서 include 한다.)
)
class CSound
{
public:
          BOOL CSound ::Play();
};

inline BOOL CSound ::Play()
{
    
}

또는

class CSound
{
public:
          BOOL CSound ::Play();
};

#include “Sound.inl

3.5          클래스 선언문에 어떠한 함수라도 구현을 하지 않는다.
(
클래스의 선언문은 매우 중요한 공간이다. 그 공간에 함수의 구현이 들어간다면 함수의 이름들을 한눈에 볼 수 없어 가독성이 매우 떨어지게 된다. 따라서 모든 구현은 cpp 파일 및 별개의 인라인 함수로 구현한다.)
잘못된 예)
class CTest
{
    BOOL Play()
    {
      
    }
};

3.6          클래스의 생성자종결자는 inline함수로 구현하지 않는다.
(
성능향상을 위한 몇몇 경우를 제외하면 생성자와 종결자는 호출되는 회수는 매우 적다. 따라서 inline으로 구현하지 않는다.)

3.7          종결자는 일반적으로 virtual로 선언한다.
(
종결자가 virtual이어야 상속될 경우 등에서 올바르게 종결자가 작동된다. 따라서 상속이 되지 않기를 바라는 경우와 클래스 크기를 줄이기 위한 경우 및 성능향상을 위한 경우를 제외하고 가상함수로 선언한다.)


4          파일 및 디렉토리

4.1          클래스명과 파일명은 ‘C’를 제외한 동일한 이름을 가진다.
)     클래스명 : CFileName
        
파일명    : FileName.h, FileName.cpp

4.2          1클래스 2파일(.h .cpp)을 가진다.

4.3          클래스의 복잡도에 따라서 여러 개의 cpp 파일을 가질 수 있다.
(
종종 멤버 함수가 많다면 함수의 검색 및 수정 등이 불편하여 관리가 어려워진다. 그러한 경우 비슷한 함수 별로 파일을 만든다면 쉽게 관리가 가능하다.)
)     클래스명 : CSurfaceGdi
        
파일명    : SurfaceGdi.h, SurfaceGdiPut.cpp SurfaceGdiInit.cpp, SurfaceGdiEffect.cpp

4.4          Inline 함수가 많이 존재할 경우 .ini 파일을 생성한다.
)     FileName.inl

4.5          매우 간단하면서 비슷한 여러 개의 클래스 및 구조체의 경우 1개의 파일에 포함될 수 있다.
(
작은 클래스, 구조체마다 각각의 파일을 가진다면 파일개수의 복잡성이 증가한다.)

4.6          문서(기획서, UML) 파일은 소스파일과 다른 특정 디렉토리를 사용한다. (:Document)
(
각종 문서들을 소스파일과 함께 관리한다면 더욱더 복잡한 파일목록들이 만들어질 것이다.)

4.7          일정 규모이상의 프로젝트의 시작은 폴더구조를 설계한 이후 시작한다.
(
프로젝트 시작 시 폴더의 구조와 각 폴더 별 연관성 등을 고려하여 폴더구조를 정의하고 상호 참조 관계 등을 정의하는 것으로 프로젝트를 시작해야 한다.)
)
- [App]
- [Common]
- [Core]
- [Sound]
- [Image]
- [Document]

4.8          #include 시 상대주소를 사용한다.   ) #include ‘../BugGame/BugMain.h
(
절대주소를 사용한다면 다른 프로그래머들은 컴파일이 안될 수도 있기 때문이다.)

4.9          #include 는 시스템 헤더 -> 라이브러리 -> 자체 코드 등의 순서로 작성한다.
(#include
또한 상위 -> 하위 관계를 지키도록 하면 가독성이 높아진다.)
)
#include ‘stdio.h
#include ‘afxinet.h
#include ‘MyImageEngine.h
#include ‘MyFile.h

4.10       백업은 다른 디스크드라이브에 받고 일정기간마다 CD 또는 별개의 매체에 저장한다.
(
백업의 중요성은 아무리 강조해도 지나치지 않다.)

4.11       프로그램 코드, 이미지, 사운드를 포함한 모든 소스는 소스세이프(SourceSafe) Subversion등의 소스관리툴을 사용한다.
(
다른 프로그래머가 소스 관리툴을 이용하더라도 컴파일과 실행을 할 수 있게 한다.)


5          레이아웃

5.1          변수 또는 함수 선언 시 TAB으로 정렬하지 않는다.
(TAB
문자를 이용한 정렬은 보기가 좋기는 하지만 결국 전체 스타일은 망가뜨린다. 또한 특정 변수의 추가 / 제거시 다른 변수들의 탭이 엉클어져 다시 정돈하는 과정이 필요하므로 불필요한 편집 작업을 늘린다.)
올바른 예)
int nCount;
int nSize;
BOOL bSuccess;
CString strName;

잘못된 예)
int                   nCount;
int                   nSize;
BOOL              bSuccess;
CString            strName;

5.2          가독성을 이유로 다음과 같은 정렬 방식은 가능하다.
int nSum =       a + b + c +
                       d + e;
if(       nCount > 100 && nSize > 50 &&
         nHit == 5 )

 

5.3          TAB의 크기는 4글자로 한다.

5.4          함수마다 1라인을 띄운다.

5.5          ‘+, -, *, %, /, <, >, =, ==, +=, -=‘ 와 같은 2개의 변수를 계산하거나 비교하는 경우에는 연산자의 전후는 1칸씩 띄어 쓴다.
(
변수 단독 붙여서 사용하는 ‘++, --, !, &(참조), *(포인터)’와 같은 연산자의 경우는 예외이다.)
올바른 예)
i = a + b – c;
nSum += nCount;
if( nSize < nMaxSize )
i++;

잘못된 예)
i=a+b–c;
nSum+=nCount;
if( nSize<nMaxSize )
i ++;

5.6          일반적으로 i++ 은 사용이 가능하지만 ++i 는 사용하지 않는다.
(
속도를 위해서 후자를 사용하는 사례들이 가끔 있지만 사실상 컴파일러의 최적화로 인하여 위 내용의 속도의 차이가 존재하지 않는다. 따라서 가독성과 통일성을 이유로 전자만 사용한다.)

5.7          소괄호의 사용은 다음과 같이 공백입력을 한다. (함수, if, while, for, switch, 형 변환 등 모두 동일함)
)
if( bSuccess ) …
int DoSomething( int nParam1, int nParam2 );
for( int I = 0; I < nCount; I++ )
int nIntSize = sizeof( int );
int nValue = ( int ) byValue;

5.8          중괄호의 사용은 다음과 같은 형식으로 한다. (함수, if, while, for, switch 모두 동일함)
올바른 예)
if( bSuccess )
{
          DoSomething();
         
}

int DoSomething()
{
         
}

잘못된 예)
if( bSuccess ){
          DoSomething();
         
}

if( bSuccess )
          {
          DoSomething();
         
          }

5.9          가독성을 위하여 다음과 같이 TAB으로 정렬이 가능하다.
(
본 가이드의 핵심은 가독성이 좋은 코드를 만드는 것을 도와주는 것이다. 따라서 그것을 위해서 몇몇 규범들은 위배 될 수 있다. 하지만 아래내용을 절대로 남용하지 않는다.)
) 
if(                    nCount == 0 )                   DoSomething();
else if(             nCount == nMaxCount )    DoSomething();
else                                                        DoSomething();

nValue =         ( nSize               * nCurrentSize )               / nDiv +
                       ( nCount             * nCurrentCount )            / nDiv +
                       ( nTime              * nCurrentTime )              / nDiv;

switch( nState )
{
          case STATE_NORMAL :   DoNormal(); break;
          case STATE_OVER :        DoOver(); break;
          case STATE_DOWN :      DoDown(); break;
}

5.10       Switch 구문은 다음과 같은 형식으로 한다. (‘:’ 의 띄어쓰기에 유의한다.)
)
switch( nState )
{
// case
관련 주석은 여기에
case STATE_NORMAL :
          //
아래 주석은 여기에
          DoSomething();
          break;

case STATE_OVER :
          DoSomething();
          break;

default :
          DoSomething();
          break;
}

5.11       한 줄 조건문반복문은 다음과 같이 모두 사용 할 수 있다. 하지만 동일 구문에 여러 스타일을 혼용하여 사용하면 안된다.
올바른 예)
if( bSuccess ) DoSomething();
else DoSomething();

if( bSuccess )
          DoSomething();
else
          DoSomething();

if( bSuccess )
{
          DoSomething();
}
else
{
          DoSomething();
}

잘못된 예)
if( bSuccess ) DoSomething();
else
{
          DoSomething();
}

if( bSuccess ) DoSomething();
else
          DoSomething();

if( bSuccess )
{
          DoSomething();
}
else   DoSomething();

5.12       ‘return’ 윗라인은 1줄 여백으로 한다.
올바른 예)
BOOL IsSuccess()
{
          BOOL bRtn = m_bRtn;

          return bRtn;
}

잘못된 예)
BOOL IsSuccess()
{
          BOOL bRtn = m_bRtn;
          return bRtn;
}

5.13       중괄호를 닫는 부분과 if, for등의 조건, 반복 구문에서는 1줄 여백을 가진다.
(
적절한 줄 여백은 코드를 읽기 쉽게 한다. 참고로 if-else구문은 else에만 적용한다.)
올바른 예)
if( bSuccess )
{
         
}
else
{
         
}

nSum += nCount;

잘못된 예)
if( bSuccess )
{
         
}
nSum += nCount;

 

6          If / switch / for / while / switch 구문

6.1          If / switch / for / while 구문 자체에서 복잡한 계산을 하지 않는다.
(
조건문 내의 복잡한 계산은 코드의 가독성을 저해한다.)
올바른 예)                                  // 잘못된 예)
nSum = 0;                                  //
for( int I = 0; I < 100; I++ )             // for( int I = 0, nSum = 0; I < 100; I++, nSum += I; )
{                                                // {
          nSum += I;                        //
          printf( “%d”, nSum );        //         printf( “%d”, nSum );
}                                                // }

hFile = open( szFileName, ‘w’ );// if( hFile = open( szFileName, ‘w’ ) )…
if( hFile )                                     // {
{                                                // }
}                                                //


6.2          Do – While 은 가능한 사용하지 않는다.
(While
만을 사용하는 것이 가독성이 증가된다.)

6.3          무한 루프 구문은 while( TRUE ) 또는 while( 1 )을 사용 한다.
올바른 예)
while( TRUE )
{
}

while( 1 )
{
}

잘못된 예)
for( ; ; )
{
}

do
{
} while( 1 )

6.4          BOOL HRESULT 등의 리턴 값을 TRUE, 0 과 비교하지 말고, ! 이나 비교 없이 단독으로 사용한다.
(
이것은 몇몇 버그가 있는 API 함수들을 위한 것이다. 즉 매뉴얼 상에는 리턴 값이 BOOL 이더라도 0 1 리턴 하지 않는 경우가 종종 있다. 따라서 일반적으로 0 인가 0 이 아닌 가로 구분하거나 본 구문과 같이 처리하는 것이 일반적이다. )
올바른 예)
if( IsMain() ) 
if( !IsMain() ) 

잘못된 예)
if( IsMain() == TRUE ) …


7          주석 (자세한 내용은 별첨 첨부)

7.1          주석은 모국어(한국어)로 작성한다.

7.2          주석은 Doxygen 표준을 사용한다.

7.3          복잡성에 따라서 세부적인 설명 및 예제를 포함할 수 있다.

7.4          일반적인 주석의 경우 /*, */ 보다는 가능한 // 으로 주석을 사용한다.
(Doxygen
의 경우를 예외로 한다.)

7.5          주석은 내용의 앞 라인과 같은 컬럼에 기록한다.
(
몇몇 특수한 상황에서 라인 뒤에 입력하는 경우도 있다.)
올바른 예)
//
성공했다면
if( bSuccess )
{
          //
어떤일을 한다.
          DoSomethinh();
}

잘못된 예)

//
성공했다면
if( bSuccess )
{
//
어떤일을 한다.
          DoSomethinh();
}

if( bSuccess ) //
성공했다면
{
          DoSomething(); //
어떤일을 한다. (X)
}


8           권장하는 함수명
일반적으로 생성, 초기화하는 부분은 BOOL 을 리턴 하고 파괴, 제거, 닫는 부분은 void 형 함수로 처리한다.
권장하는 함수명 뒤에 CreateImage, OpenFile과 같이 각종 명사를 붙여서 사용 할 수 있다.

8.1          Create / Destroy           생성 / 파괴

8.2          Open / Close                열다 / 닫다

8.3          Init / Final                      초기화 / 마지막

8.4          Play / Stop                   시작 / 정지

8.5          Get / Set                      얻음 / 넣음

8.6          Add / Remove              추가 / 삭제 (일반적으로 순서는 상관 없다)

8.7          Insert / Delete              (중간에) 삽입 / (중간에) 삭제

8.8          Start / Stop                   시작 / 정지

8.9          Suspend / Resume      일시 정지 / 재 시작

8.10       Begin / End                  시작 /

8.11       First / Last                    처음 /

8.12       Next / Previous             이전 / 다음

8.13       Increment / Decrement 증가 / 감소

8.14       Old / New                     이전 / 새로

8.15       Up / Down                    /

8.16       Min / Max                     최소 / 최대

8.17       Show / Hide                 보여줌 / 가림



참고 :
http://geosoft.no/development/cppstyle.html

http://blog.naver.com/iliyard/9298516

http://blogs.msdn.com/brada/articles/361363.aspx

Effective C++, Scott Meyers

More Effective C++, Scott Meyers

Code Complete, Steve McConnell

출처 - http://tong.nate.com/swallow/

'Programming > C/C++' 카테고리의 다른 글

[C/C++] 복합대입연산자  (1) 2010.01.14
[C/C++] 문자열 라이브러리 함수 구현  (0) 2010.01.13
[C/C++] sprintf  (0) 2010.01.13
[C/C++] 문자열 복사  (0) 2010.01.13
Posted by 시그v

error LNK2005: "public: __thiscall Cdefine::Cdefine(void)" (??0Cdefine@@QAE@XZ) already defined in Fluid_Simulation_menu.obj

error LNK2005: "public: __thiscall Cdefine::~Cdefine(void)" (??1Cdefine@@QAE@XZ) already defined in Fluid_Simulation_menu.obj

 

헤더 파일에 함수의 정의해서 그렇다.

에러를 보면 Cdefine(void)와 ~Cdefine(void) 함수가 헤더파일에 정의되어서 뜨는 에러이다.

'Programming > Visual Studio' 카테고리의 다른 글

error LNK2005:  (0) 2010.01.13
error LNK2059  (0) 2010.01.13
error LNK1104  (1) 2010.01.13
error LNK2019 fatal, error LNK1120  (0) 2010.01.13
VisualStudio 메모리 누수 체크 사용하기  (0) 2009.12.19
Posted by 시그v

1. 충돌처리
· 직선 교차점 알고리즘

1.        직선을 정의하는 두 점(선분의 시작점과 끝점)이 모두 평면의 한쪽 방향에 위치하고 있다면 직선은 
절대로 평면과 교차할 수 없기 때문에 이 두 점이 폴리곤의 양쪽 방향에  존재하는지를 알아본다.
2.        폴리곤을 정의하는 평면과 직선의 교차점을 계산한다.
3.        평면과 직선의 교차점이 폴리곤 영역 안에 포함되어 있는지를 검사해서 폴리곤 영역에 포함될 경우 
충돌을 나타내고 그렇지 않을 경우 충돌하지 않은 것을 나타낸다.

· 평면과 직선의 교차
직선을 정의하는 두 점이 폴리곤을 정의하는 평면의 양쪽에 존재하는지를 알아보는 첫번째 과정은 상당히 쉽다. 
수학에서 정의하는 3차원 평면에 대한 방정식으로 유도할 수 있다.

평면의 방정식
Ax + By + Cz + D = 0
위의 식에서 A, B, C는 평면의 노말 벡터의  x, y, z 요소를 각각 나타내고 D는 폴리곤 위에 존재하는 임의의 
한 점을 방정식에 대입해서 구할 수 있는 값이다.

평면의 방정식의 특징은 평면위에 존재하는 어떤 점을 x, y, z에 대입하더라도 항상 0 이 된다. 이러한 특성을 
이용하여 충돌처리를 할 수 있는데 평면의 방정식의 특성상 평면의 뒤쪽에 존재하는 점을 방정식의 x, y, z에 
대입한다면 음수 값을 가지고 앞쪽에 존재하는 점을 대입하면 양수 값을 갖게 된다. 따라서 직선을 정의하는 두 
점이 평면의 양쪽에 존재하는지를 알아보기 위해서는 단순히 두 점을 평면의 방정식에 대입해서 그 부호가 서로 
다른지를 알아보면 된다.
그럼 다음 함수를 보자
bool LineCrossPlane( Vector *line, Vector &normal, float &D )
{
        float        sign1, sign2;

        sign1 = (normal.i * line[0].x + normal.j * line[0].y + normal.k * line[0].z + D);
        sign2 = (normal.i * line[1].x + normal.j * line[1].y + normal.k * line[1].z + D);

        if( sign1 * sign2 >= 0.0 )
         return false;
        
return true;
}
위의 말을 더 쉽게 풀이해 보면
한 삼각형(평면)의 Face Normal을 Nx, Ny, Nz라고 정의하구 삼각형(평면)의 한점을 Px, Py Pz 라고 한다면, 
삼각형의 노말과 그 삼각형에 속하는 한점을 DotProduct를 하면 0 이 된다.이 말을 평면의 방정식에 대입하면 
이렇게 된다...
Nx * Px + Ny * Py + Nz * Pz + D = 0 ........ (1)
(평면의 방정식 A*x + B*y + C*z + D = 0 )

여기서
A = Nx
B = Ny
C = Nz
D = -( Nx * Px + Ny * Py + Nz * Pz )
Px, Py, Pz 는 평면에 속하는 한점이다.

· 교차점 계산
직선을 정의하는 두 점이 평면의 양쪽에 존재한다면 반드시 직선과 폴리곤이 교차하는 교차점이 존재한다고 
할 수 있다. 이러한 교차점을 구하기 위해서는 평면과 직선을 모두 방정식으로 표현해야 한다.

평면의 방정식
Ax + By + Cz + D = 0

직선을 방정식으로 표현하는 방법은 여러 가지가 있지만 그 중에서 직선 위에 존재하는 모든 점을 가장 간편하게 
나타내는 방정식은 직선의 시작점으로 부터의 거리를 이용하는 방법이다.

P(d) = Po + dD
위의 식에서 D는 직선의 방향을 나타내는 단위 벡터이고 P(0)는 시작점을 그리고 d는 P(0)으로부터 D 벡터가 정의
하는 방향으로 떨어져 있는 거리를 나타낸다. 따라서 P(d)는 P(0)으로부터 d만큼 D방향에 떨어져 있는 직선 위의 
한 점을 나타낸다. 위에서 정의한 일반식을 x, y, z 요소로 분할하면 다음과 같음을 알 수 있다.

Px = xo + dDx
Py = yo + dDy
Pz = zo + dDz
위에서 Px, Py, Pz는 직선의 시작점 xo, yo, zo로부터 Dx, Dy, Dz 방향으로 d만큼 떨어져 있는 직선 위의 한 점에 
대한 x-, y-, z- 요소를 나타낸다.

그럼 임의의 한 점에 대한 평면의 방정식을 써보면
Axo + AdDx + Byo + BdDy + Czo + CdDz + D = 0
위와 같이 된다.
만일 직선 위의 한 점 Px, Py, Pz가 평면위에 존재한다면(교차점이라면) 이 방정식의 왼쪽 항은 0값을 갖게 된다. 
따라서 위의 방정식으로부터 교차점을 찾기 위해서 먼저 상수 d에 대해 정리하면

d(Adx + BDy + CDz) + Axo + Byo + Czo + D = 0                 이것을 다시 정리하면
d = -(Axo + Byo + Czo + D) / (ADx + BDy + CDz)

Dx, Dy, Dz는 직선의 방향을 나타내는 단위 벡터이고 A, B, C, D는 평면을 나타내는 방정식의 계수다. 또 xo, 
yo, zo는 직선의 시작점을 나타내기 때문에 모두 상수임을 알 수 있다. 따라서 아무 문제없이 위의 방정식으로부터 d값을 구할 수 있다.

¨ NOTE
만일 평면과 직선이 서로 평행이라면 위의 방정식의 분모는 0 이 된다. 그 이유는 위 방정식의 분모를 보면 
평면에 수직인 법선벡터(A, B, C)와 직선의 방향을 나타내는 벡터(Dx, Dy, Dz)에 대한 내적임을 알 수 있다. 
만일 평면과 직선이 평행이라면 평면에 수직인 법선 벡터와 직선의 방향 벡터가 이루는 각은 90도이다. 따라서 
내적 정의에 의해 0이 된다.

함수는 다음과 같다.
bool  LineIntersectPlane( Vector &point, Vector *line, Vector &normal, float &D )
{
        Vector line2 = line[1] – line[0];
        float        d, first, second;
        
        line2.Normalize();        // 단위벡터로 만든다.

        first = -( normal.i*line[0].x + normal.j*line[0].y + normal.k*line[0].z + D );
        second = ( normal.i * line2.i + normal.j * line2.j + normal.k * line2.k );

        if( second == 0 )
          return false;
        
        d = first / second;

        if( d < 0 )
          return false;

        point = (line[0] + (Vector)(line2 * d));
        return true;
}

· 폴리곤 영역에서 충돌이 일어났는지 판별
폴리곤 영역에서 충돌이 일어났는지 판별하기 위해서는 교차점과 폴리곤의 Vertex가 이루는 각의 합을 계산해서 
360도와 같다면 영역에 포함된다고 할 수 있다.

내적 공식
A · B = |A| |B| cosq
위에서 q는 두 벡터가 이루는 각을 나타낸다. 따라서  이 방정식을 q에 대해 풀면 다음과 같다.

q = cos¯¹ (A · B / (|A| |B|))
위에서 cos¯¹은 코사인 함수의 역함수를 나타낸다. 폴리곤의 버텍스로부터 벡터 A와 B를 계산하기 위해서는 
단순히 각 버텍스에서 교차점을 빼면 된다.

내적 계산
A·B = x1x2 + y1y2 + z1z2

함수로 표현하면
bool  IsPointBounding( Vector &point, Vector  *Poly, long VertexCount )
{        
float Angle = 0.0f;

const MATCH_FACTOR = 0.99;
Vector A, B;

for( long n = 0; n {
        A = (Vector)(Poly[n] – point);
        B = (Vector)(Poly[(n+1) % VertexCount] – point);
        Angle += acos( A.Dot(B) / (A.Mag() * B.Mag()));
}

if( Angle >= (MATCH_FACTOR * (2.0 * PI)) )        // 360도는 2PI
  return true;
        
return true;
}


위의 세가지 과정을 통합한 것을 함수화 하면
bool LineIntersectsPolygon( Vector *line, Vector *poly, long VertexCount, Vertex &iPoint )
{
float D;
Vector          PolyNormal;

MakeNormal( poly, PolyNormal, D );

if( !LineCrossPlane( line, PolyNormal, D ) )
        return false;

LineIntersectPlane( iPoint, line, PolyNormal, D );

if( !IsPointBounding( iPoint, poly, VertexCount ) )
        return true;
}

· 직선 교차점 알고리즘을 이용한 폴리곤 충돌 검사
위에서 말한 직선 교차점 알고리즘은 직선과 폴리곤의 충돌을 알아보는 방법이었다. 그렇다면 지금부터 이 
알고리즘을 이용해 폴리곤간의 충돌검사 알고리즘을 만들어 보도록 한다. 직선 교차점 알고리즘을 이용해서 
폴리곤 충돌 검사를 하는 것은 매우 단순한 작업이다. 먼저 충돌 검사를 수행할 폴리곤을 A, B라 하고 폴리곤 
A의 각 경계선을 직선으로 정의한다. 그리고 이렇게 정의된 직선들과 폴리곤 B를 직선 교차점 알고리즘을 
이용해서 각각 충돌 검사를 한다. 충돌 검사에서 사용된 직선들은 폴리곤 A의 경계선을 나타내기 때문에 
이들 중 어느 하나라도 폴리곤 B와 교차한다면 두 개의 폴리곤은 서로 교차한다고 말 할 수 있다. 그러나 
어느 한 폴리곤만을 기준으로 처리할 경우 두 개의 폴리곤이 서로 교차함에도 불구하고 교차점을 알아낼 수 
없는 상황이 발생하게 된다. 따라서 A, B 폴리곤의 경계선을 모두 상호 비교해야 한다.

이것을 함수화 하면
bool  PolygonIntersect( Vector *A, long AVertexCount, Vector *B, long BvertexCount, Vector &iPoint )
{
long n;
Vector         Edge[2];

for( n = 0; n < AVertexCount; n++ )
{
  Edge[0] = A[n];
  Edge[1] = A[(n+1) % AVertexCount];
if( LineIntersectsPolygon( Edge, B, BvertexCount, iPoint ) )
        return true;
}

for( n = 0; n < BVertexCount; n++ )
{
  Edge[0] = B[n];
  Edge[1] = B[(n+1) % BVertexCount];
if( LineIntersectsPolygon( Edge, A, AvertexCount, iPoint ) )
        return true;
}

return false;
}

· 복잡한 충돌검사 처리
충돌 검사 알고리즘은 폴리곤에 평행하게 접근하는 직선과 충돌하기 바로 직전에는 평면의 앞쪽에 있다가 
충돌 후에는 폴리곤의 반대편으로 이동하는 경우 충돌을 정확하게 알아내지 못한다. 보통의 경우 이러한 
문제는 따로 처리할 필요가 없다. 왜냐하면, 가상세계의 객체는 복잡한 폴리곤으로 구성되기 때문에 객체를 
구성하는 폴리곤 중 하나라도 충돌이 포착되면 충돌처리를 해주면 되기 때문이다.

그러나 객체의 이동 속도가 굉장히 빠르다거나 혹은 사용자의 컴퓨터가 느릴 경우 객체의 어떤 폴리곤도 
충돌 객체에 걸리지 않고 빠져나가는 경우가 발생하게 된다. 이 문제를 해결하기 위한 가장 간편한 방법은 
첫번째 프레임에서의 객체의 위치와 다음 프레임에서의 객체의 위치를 연결하는 직선을 이용하는 것이다. 
이 직선 중에 하나가 다른 물체와 교차한다면 두 객체가 충돌한 것이기 때문에 물체를 이동시키지 말아야 한다.

· 충돌 검사를 위한 최적화
많은 폴리곤에 대하여 충돌검사를 수행한다면 수많은 수학적 연산에 의해 수행 속도를 떨어뜨릴 위험이 있기 
때문에 충돌검사 최적화를 해주어 충돌검사 회수를 감소시킬 수 있다.
첫번째 최적화 방법은 각 폴리곤과 객체에 대한 바운딩 스피어를 이용하는 방벙이다. 움직이는 객체의 바운딩 
스피어를 가상세계에 존재하는 다른 객체의 바운딩 스피어와 먼저 검사해서 서로 충돌이 발생했다면 각 객체가 
갖는 폴리곤의 바운딩 스피어와 다시 비교해서 충돌이 발생한 폴리곤을 찾는다. 마지막으로 이렇게 찾은 
폴리곤에 대해서 다시 라인 교차점 알고리즘을 이요해 정확한 교차점을 계산한다. 이방법은 바운딩 스피어를 
이용해 비교하는 범위를 줄여 가는 최적화 기법이라 할 수 있다. 따라서 이 방법을 사용할 경우 객체가 갖는 
모든 폴리곤에 대해 라인 교차점 알고리즘을 적용해야 하는 비효율을 줄일 수 있게 된다. 두개의 바운딩 
스피어가 교차하는지를 알아보기 위해서는 두 바운딩 스피어의 중심간의 거리를 계산하면 된다. 만일 이 
거리가 두 바운딩 스피어의 반지름의 합보다 작다면 두개의 구는 서로 교차하고 있다.

¨NOTE
폴리곤 또는 객체에 대한 바운딩 스피어를 계산하기 위해서는 먼저 바운딩 스피어의 중심점으로 사용되는 
객체의 중심점을 계산해야 한다. 모든 버텍스의 x, y, z값을 각각 더한 값을 버텍스의 개수로 나누면 객체의 
중심점을 구할 수 있으며, 이 중심으로부터 가장 먼 거리에 있는 버텍스와 거리가 바로 바운딩 스피어의 
반지름이 된다.

두 번째 최적화 방법은 가상세계를 작은 단위로 분할하는 방법이다. 이렇게 분할된 공간 안에 속하는 폴리곤을 
리스트로 관리하면서 충돌 검사를 할 때는 이 영역에 포함되는 폴리곤에 대해서만 충돌 검사를 수행하는 방법이다. 
이 방법을 사용하게 되면 특정 객체가 존재하는 가상세계의 일부 영역에 대해서만 충돌 검사를 할 수 있기 때문에 
충돌 검사를 해야하는 횟수를 크게 줄일 수 있는 장점이 있다. 하지만 복잡한 자료구조를 구성해야 한다는 단점이 있다.

· 바운딩 박스
바운딩 박스를 만들 때 우선 모든 면의 노말이 밖을 바라보게 바운딩 박스를 만들던지 아니면 모든면이 안으로 보는 
바운딩 박스를 만들어야 한다. 그럼 평면에 방정식을 이용해서 바운딩박스의 각각의 면에 한점이 면의 앞에 있는지
뒤에 있는지를 알수가 있을 것 이다. 만약 모든 바운딩박스의 면을 밖으로 바라보게 하였다면 한점이 바운딩박스의 
모든면에 대해서 평면의방정식 < 0 을 성립한다면 그 한점은
바운딩 박스에 안에 들어 있는 것임을 알수 있다.

· 바운딩 충돌
바운딩 박스는 8점으로 이루워져 있으므로 바운딩박스 & 바운딩박스의 충돌은 한쪽의 바운딩 박스에 다른쪽의 
바운딩박스의 8점을 검색해서 충돌여부를 판단하구 충돌이 없다면 이번에는 반대로 한번 더 검사해서 최종 결정을 
내리면 된다.

· 몇가지 주의점
바운딩박스가 만약 움직이는 물체에 붙어있다면 그 물체가 움직이고 회전하는 값을 그대루 바운딩 박스에 적용해야 
합니다. 바운딩 박스가 회전을 하든지 움직일때 마다 항상 새로 바운딩박스의 면의 노말을 구해서 평면의 방정식에 
사용해야 한다. 그래야 정확한 충돌을 찾을 수 있다.


[원본] http://expert3d.egloos.com/598593
Posted by 시그v
이전버튼 1 2 3 이전버튼

블로그 이미지
Computer graphics & Programming
시그v

공지사항

Yesterday
Today
Total

달력

 « |  » 2024.5
1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 31

최근에 올라온 글

최근에 받은 트랙백

최근에 달린 댓글

믹시