////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

 

윈폼이라는 것에 대해서 알아봐야 할것이다.

 

MFC를 해본 나로서는 윈폼과 그에 대한 이벤트는 그렇게 까지 난해하게 다가오지는 않았다.

 

실제 MFC의 이벤트등도 VS스튜디오의 툴 내에서 손쉽게 지정이 가능하다.

 

VS C#에서도 이에대해서 충실하게 지원해준다.

 

 

 

보면 왼쪽에 있는 툴박스에서 버튼 컨트롤을 선택한후 오른쪽에 보이는 속성창에서 이벤트등을 생성해 줄수 있다.

 

MFC와 거의 동일한 인터페이스 이다.

 

이벤트란 간단히 말해서

버튼을 눌렀다.

창을 키웠다.

창을 움직였다 등을 의미합니다.

 

그러한 이벤트가 벌어졌을시 특정한 행동을 해라. 라는것이 기본적인 이벤트의 사용법 입니다.

 

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

 

그러한 이벤트가 벌어졌을시 특정한 행동을 해라. 라는것이 기본적인 이벤트의 사용법 입니다.

Posted by JJOREG

이번에는 explicit 키워드에 대해 얘기해 보려고 합니다.

최근에 C++ 를 배우신 분들은 모르시겠지만 explicit 키워드는 C++의 첫번째 표준안인 98년 9월 1일 버전에 처음으로 도입된 키워드입니다. Bjarne Stroustrup 이 처음에 C++ 를 창시할 때는 없었다는 거죠. 그럼 왜 98년 C++ 표준안을 출판할 때는 explicit 라는 키워드를 넣었을까요 ? 이런 의문을 제기하지 않고 왜 그런지를 모른다면 C++의 참맛을 안다고 할 수 없겠죠.

결 론부터 말씀드린다면 explicit 키워드는 C 나 C++ 의 공통 특징 중의 하나인 암시적 형변환의 특성때문에 프로그래머가 예상치 못하는 형변환이 일어남으로 인해 정말 발견하기 어려운 버그가 생기는 걸 방지하기 위해서 도입된 키워드입니다.

결론은 그렇다 치는데, 도대체 어떤 버그이길래 그렇게 발견하기 어려운 버그일까요 ? 역시 백마디 말보다 하나의 예가 이해를 돕는데는 제격이므로 예를 들어 한 단계씩 차분히 설명해 보도록 하겠습니다.

일 단, 여러분이 Stack 이라는 클래스를 설계하려고 한다고 해 보죠. 제일 먼저 있어야 할 것은 Stack 객체를 생성하는 생성자가 있어야 할 것입니다. 우선 생각할 수 있는 생성자로는 n 개의 요소까지 가질 수 있는 Stack 을 생성하는 걸 생각해 볼 수 있을 것입니다. 다른 생성자로는 다른 Stack 을 복사해서 생성하는 것, 다른 배열을 복사해서 생성하는 것, 다른 vector 를 복사해서 생성하는 것 등을 생각할 수 있을 것입니다. 이런 생성자를 갖는 Stack 은 다음과 같이 작성할 수 있겠네요.

// 저번에 썼던 코드입니다.
template <typename T>
struct Print {
  void operator() (T v) {
    cout << v << " ";
  }
};
 
class Stack {
private:
  vector<int> _data;
  int _pos;
 
public:
  const static int MIN_ELEM = 10;
 
  // 최대 n 개 요소를 가질 수 있는 Stack 생성자
  // 기본값을 지정해 봤습니다.
  Stack(int n = MIN_ELEM): _data(n), _pos(0) {
    cout << "Stack(int) called" << endl;
  }
 
  // 다른 Stack 을 복사해서 생성하는 복사 생성자
  Stack(const Stack& other): _data(other._data), _pos(other._pos) {
    cout << "Stack(const Stack&) called" << endl;
  }
 
  // 다른 배열을 복사해서 생성하는 생성자
  Stack(int arr[], int n): _data(arr, arr+n), _pos(n) {
    cout << "Stack(int[], int) called" << endl;
  }
 
  // 다른 vector 를 복사해서 생성하는 생성자
  Stack(vector<int> v): _data(v), _pos(v.size()) {
    cout << "Stack(vector<int>) called" << endl;
  }
 
  // 그냥 어떻게 작동하는지 확인하기 위한 디버깅용 멤버 함수
  void printAll() {
    for_each (_data.begin(), _data.end(), Print<int>());
    cout << endl;
  }
};

다음 코드를 수행하면 어떤 결과가 벌어질까요 ? 여러분의 예상과 맞아 떨어지는지 한 번 보시기 바랍니다.

01: int
02: main(void)
03: {
04:   Stack s1;
05:   s1.printAll();
06:   Stack s2(20);
07:   s2.printAll();
08:   Stack s3(s2);
09:
10:   vector<int> v(10, 3);
11:   for_each(v.begin(), v.end(), Print<int>());
12:   cout << endl;
13:
14:   Stack s4(v);
15:   Stack s5 = v;
16:   s1 = v;                 // 이게 어떤 효과를 일으킬까요 ?
17:   s2 = 10;                // 버그 아닌가요 ? 좀 이상하지 않나요 ?
18:
19:   s1.printAll();          // 뭐가 찍힐까요 ?
20:   s2.printAll();          // 뭐가 찍힐까요 ?
21: }

위 코드가 컴파일될까요 ? 특히 17 line은 컴파일이 안될 것 같지 않으세요 ? stack 에 정수값을 할당한다는 게 전혀 말이 안되니 당연히 컴파일 안되어야 정상이겠죠. 자 한 번 컴파일해 볼까요 ? 어! 이게 어떻게 된 거죠 ? 아무런 에러 없이 컴파일이 되네요. 17 line 도 에러 없이 컴파일이 됩니다. 이게 어떻게 된 조화일까요 ? 컴파일러가 왜 이걸 정상적인 코드로 인식하고 컴파일을 했는지를 알려면 우리의 입장이 아니라 컴파일러의 입장에서 생각해 봐야 할 겁니다. 이른바 역지사지 해 보는 것이죠.

17 line 을 만나면 컴파일러는 어떻게 할까요 ? 컴파일러가 하는 일은 자신이 알고 있는 모든 지식을 동원해서 코드를 컴파일 해내는 것입니다. 먼저 s2 에 정수값을 할당할 수 있는 할당 연산자가 있는지 볼 것입니다. 어! 그런데 Stack 클래스 정의를 봤더니 아무리 눈씻고 찾아 봐도 없네요. 프로그래머가 정의한 할당 연산자가 없다면, 컴파일러가 생성한 복사 할당 연산자가 있을 것입니다. 자기가 생성한 복사 할당 연산자를 쓰려고 했더니, 이번에는 10 이라는 정수값이 복사 할당 연산자의 인자와 호환이 되질 않네요. 그렇다고 우리의 C++ 컴파일러는 그냥 컴파일을 포기하고 항복할 녀석이 아니죠. 이번에는 혹시 정수를 복사 할당 연산자의 인자(const Stack& 일 겁니다)로 변환할 수 있는지를 볼 겁니다. 여기에서 C++ 컴파일러는 인자가 하나인 생성자를 암시적 형변환하는데 사용한다는 것을 기억해 보세요. 죽 뒤져 보았더니 Stack(int) 가 눈에 띄네요. 그래서 결국 Stack(10) 을 호출해서 10개의 요소를 갖는 Stack 을 생성하고 그걸 복사 할당 연산자를 이용해서 s2 에 할당하게 됩니다. 이제 그 짧은 코드 안에서 어떤 일이 일어나는지 이해가 되시나요 ?

제가 쓰고 있는 컴파일러(Microsoft Visual C++ 2005)에서 컴파일 후 실행해 봤더니 다음과 같은 결과가 나옵니다.

01: Stack(int) called                         // 04 line 출력
02: 0 0 0 0 0 0 0 0 0 0                       // 05 line 출력
03: Stack(int) called                         // 06 line 출력
04: 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0   // 07 line 출력
05: Stack(const Stack&) called                // 08 line 출력
06: 3 3 3 3 3 3 3 3 3 3                       // 10 line 출력
07: Stack(vector<int>) called                 // 14 line 출력
08: Stack(vector<int>) called                 // 15 line 출력
09: Stack(vector<int>) called                 // 16 line 출력
10: Stack(int) called                         // 17 line 출력
11: 3 3 3 3 3 3 3 3 3 3                       // 19 line 출력
12: 0 0 0 0 0 0 0 0 0 0                       // 20 line 출력

09 ~ 12번째 라인 결과가 좀 의아하지 않나요 ? 왜 이런 결과가 나올까요 ? 눈치 빠른 분들 벌써 회심의 미소를 짓고 계시는 군요. 예, 그렇습니다. 아까 컴파일러 입장에서 살펴 본대로 C++ 의 암시적 형변환 규칙에 따르면 클래스의 생성자 중에 인자가 하나인 생성자들은 프로그래머의 의도와는 전혀 상관없이 컴파일러에 의해 암시적 형변환에 동원되도록 되어 있어서 s1 = v, s2 = 10 이라는 코드를 만나면 각각 Stack(vector<int>), Stack(int) 를 호출하여 임시 객체를 생성하고, 그 임시 객체를 가지고 복사 할당 연산자를 호출하게 됩니다(위 코드 예에서는 컴파일러에 의해 자동 생성된 할당 연산자가 호출되겠죠. 클래스 정의에서 할당 연산자를 여러분 나름대로 정의한 후, cout 으로 뭔가를 출력하게 되면 할당 연산자가 호출되는 것을 확인하실 수 있을 겁니다). 그러고 나면 s1 은 v vector의 내용으로 Stack 을 채우게 될 것이고, s2는 10개의 int 요소로 채우게 될 것입니다. 그래서 결과적으로 09 ~ 12번째같은 결과가 나오는 것이지요. s1 = v 에 대해 Stack(vector<int>) 를 호출하는 건 그래도 어느 정도 말이 된다고 하지만 s2 = 10 에 대해 Stack(int) 를 호출하는 건 좀 오버아닌가요 ? 도대체 Stack 객체에 10을 할당한다는 게 말이 되느냔 말입니다.

C++ 컴파일러는 참 잘난체 하는 데는 이골이 났나 봅니다. 프로그래머가 특별히 정의하지 않더라도 기본 복사 생성자, 복사 할당자를 생성해 버리고, 그것도 모자라 인자가 하나인 생성자를 암시적 형변환하는데 지 맘대로 써 버린다니… 그런데 나름 C++ 컴파일러도 말 못할 고민이 있다고 하네요. C++ 본래 태생이 그래서 그렇답니다. 맘에 안든다고 버릴 수도 없고, 그냥 우리가 적응해서 살아야 되지 않겠습니까 ?

아무리 적응하려고 한다지만 s2 = 10 을 만났을 때 Stack(int) 를 호출하는 건 프로그래머 입장에서는 참 예상하기 힘든 일입니다. 그래서 이런 예상치 못한 수행이 일어나면 프로그래머는 그런 버그를 정말 찾기 힘들게 됩니다. 실제로 이런 일들이 프로젝트 수행 도중 종종 일어나고, 이 버그는 찾기도 정말 힘듭니다. 그러니 아예 이런 일은 피해가는 게 상책이지요. 피해가는 방법이야 s2 = 10 과 같은 코드를 작성하지 않도록 Coding Guideline 에 넣어 두고, Code Review 도 하고 그런 방법도 있겠지만 좀 더 좋은 방법은 아예 s2 = 10 과 같은 코드를 만나면 컴파일시에 에러가 발생해 버린다면 더 좋을 것입니다(항상 기억하세요! 에러는 빨리 발견할 수록 수정하는데 드는 비용이 작다).

그래서, explicit 키워드가 C++의 첫번째 표준안에 새로 도입됐던 것입니다. explicit 키워드는 인자가 하나인 생성자가 암시적 형변환에 쓰이지 않도록 해줍니다. 암시적 형변환에 쓰이면 컴파일 에러가 발생하게 됩니다. 위에 정의한 Stack 정의에서 다음 부분을 수정하신 후에 main()함수를 다시 컴파일해 보시기 바랍니다.

  // 최대 n 개 요소를 가질 수 있는 Stack 생성자
  // 기본값을 지정해 봤습니다.
  explicit Stack(int n = MIN_ELEM): _data(n), _pos(0) {
    cout << "Stack(int) called" << endl;
  }

제가 쓰는 컴파일러에서는 다음과 같은 에러가 발생하네요.

error C2679: binary '=' : no operator found which takes a right-hand operand of type 'int' (or there is no acceptable conversion)

정확히 의도한 바와 같이 컴파일 에러가 발생하네요.

이 상에서 밝힌 바와 같이 explicit 키워드는 C++ 의 특징 중에 하나인 암시적 형변환 때문에 발생할 수 있는 문제의 소지를 미리 예방하는데 유용한 키워드입니다. 그러니, 클래스의 생성자를 설계할 때, 인자가 하나만 있는 생성자에 대해서는 암시적 형변환이 발생할 때 어떤일이 벌어질지를 생각해 보고, 암시적 형변환이 말이 안되는 의미를 갖게 될 경우 explicit 라는 키워드를 꼭 붙이시기 바랍니다. 아니면 거꾸로 생성자에 대해 일단은 무조건 explicit 키워드를 붙였다가 컴파일할 때 암시적 형변환이 허용되지 않아서 너무 불편하다 싶으면 그때가서 빼는 것도 방법입니다. explicit 키워드를 쓴 경우에는 다음과 같이 코드를 작성하시면 암시적 형변환과 같은 의미를 갖는 코드를 작성하실 수 있습니다.

s1 = Stack(v);
s2 = Stack(10);

마지막으로 다음을 기억하신다면 C++의 암시적 형변환 때문에 버그 찾느라 헤메는 일은 없을 것입니다.

'게임개발공부 > 무작정퍼오기' 카테고리의 다른 글

컴파일러 인터프린터  (0) 2014.01.13
함수객체  (0) 2013.12.29
DLL만 로딩해도 컴퓨터가 뻗는경우.  (0) 2013.12.21
volatile (퍼온글)  (0) 2013.12.21
extern "C" 이건 뭔가? (퍼온글)  (0) 2013.12.21
Posted by JJOREG

프로세스 모형

개요

장점

단점

Waterfall Model

- 순차적 모델(Sequential Model) 또는 고전적 생명주기 라고도

계획, 분석, 설계, 코딩(구현), 시험, 유지보수의 순으로 관리되는 모형으로 단계의 완벽한 작업 종류를 전제로 하는 모델임

가장 널리 많이 사용

단계별로 정형화된 접근방식

체계적인 문서화, 단계별 산출물 체크를 통한 프로젝트 진행의 명확화

기술적인 위험이 없고 유사한 프로젝트를 경험이 있어서 비교적 정확한 비용 예측과

일정계획 하에 프로젝트를 추진 경우 적합

문서 중심의 개발 접근 방식으로 인한 개발자의 문서화에 대한 부담과 가중

고객의 요구사항과 불일치 가능성

피드백 과정이 없어 변경이 어려움

완벽한 분석이 요구됨

대기 상태 발생으로 인한 일정 지연 가능성

Evolutionary Development Model

반복적 개발 모델의 형태

시스템이 가지는 여러 구성요소의 핵심부분을 개발한 구성요소를 개선 발전시켜 나가는 방법

- 고객의 즉각적인 요구를 반영

- 프로세스가 보이지 않는다.

- 계속적인 변경에 의하여 구조가 훼손되어 시스템이 종종 제대로 구조화되지 못한다.

Prototyping Model

- 반복적인 시제품 개발로 사용자 요구사항 또는 기술적 타당성을 확인 본격적으로 개발하는 모델

결과가 가시적이고 이해가 쉬워서 관리가 용이

사용자의 요구사항을 빠르게 수용가능

사용자의 요구사항을 확인 가능

정적인 요구명세 문서화 방법대신 실질적으로 수행되는 물리적 모형 확인

최종 소프트웨어 제품을 완성하기 전에 시제품을 완제품으로 발전하게 가능성

사용자의 과도한 요구

시제품 폐기의 비경제성

반복적인 시제품 개발의 종료 시기 문제

Spiral Model

진화적인 소프트웨어 프로세스 모델로써, 시제품화 모델의 반복적인 개발이라는 특성과 폭포수 모델의 체계적인 관점지원 이라는 특성을 결합

대규모 시스템 개발에 대한 효율적인 접근으로 위험 분석 추가함

기존의 소프트웨어 생명주기 모형의 장점들의 선택적 수용을 허용

위험관리(Risk Management) 위주의 접근 방식

대규모 시스템 구축에 적합

모델 자체가 복잡하여 프로젝트 관리가 어렵고 고객을 설득시키기 어렵다

많은 고객을 상대로 하는 상업용 제품에 적용하기 힘들다.

상대적으로 새로운 접근 방법이며 많이 사용되지 않아 충분한 검증을 거치지 못함

많은 위험 평가 전문가 필요

주된 위험이 발견되지 않을 문제 발생

 

Incremental Development Model

폭포수 모델에 반복적 수행 개념을 결합

- 전반적인 프로젝트 요구사항을 리스크에 따라 우선순위를 정하여증분(increments)’으로 나누고, 이를 점진적으로 개발하여 순차적으로 기능의 일부를 릴리즈하는 개발 수명주기(life cycle) 모델

여러 개의 팀이 나누어 개발 하고자 유용

- 고객 서비스의 신속한 인도

- 사용자가 시스템 개발에 참여

- 완전한 시스템 명세서가 최종 산출물이 지정될 때까지 존재하지 않아 완전한 시스템 명세서가 시스템 개발 계약의 일부분인 경우 적용 불가능

Rapid Application Development Model

짧은 개발주기 동안 소프트웨어를 개발하기 위한 모델

불필요한 활동 작업을 제거하여 프로세스를 간결화

- 짧은 기간 동안에 개발 가능

- 테스트 기간이 짧음

- 검증된 컴포넌트의 재사용

- 4세대 언어 또는 비주얼 사용

- 대규모 프로젝트의 경우 충분한 인적자원 경영진의 지원요구

- 긴급한 활동을 위해 간부급의 위원회 조직 필요

- 기술적 위험이 크고 고성능이 요구되는 시스템에 부적합

V-Model

- 일반 폭포수 모델의 사례로 시험 계획이 시스템 명세서와 설계로부터 유도되어야 한다는 것을 보여주는 모델

- 실행 작업과 작업결과의 검증에 초점을 프로세스 모델

- 단계적 검증 절차를 갖기 때문에 Safety-critical System 적용 가능

- 테스트 단계의 오류 발견 요구명세, 분석, 설계, 구현으로 되돌아 있는 추적성 (Traceability) 보장

- 시험 계획 수립에 시간과 비용이 많이 소모됨

 

 

 

'게임개발공부 > 프로그래밍의 의견들' 카테고리의 다른 글

사용자 스토리 & 유즈 케이스  (0) 2014.07.30
프로그래밍 설계상의 부채  (0) 2014.07.29
밸런싱 관련 의견.  (0) 2014.01.11
의견 1  (0) 2013.11.27
Posted by JJOREG

Chain of Responsibility(체인 오브 레스포빌리티) 패턴 --------------------------------------------------------------------------------------------

  어떠한 객체를 생성하고 그 인스턴스에 인자값을 통해서 어떠한 요청을 하면 함수는 그에 대한 처리를 할 것입니다.

  하지만 인자값이 분명한데도 잘못된 객체에 집어넣거나.

  혹은 함수에 존재하는 예외처리가 되어 버리는 경우가 있습니다.

  또는 어떠한 요청에 대한 처리가 너무 많아서 하나의 클래스의 크기가 비대해 지는 경우가 있습니다.

  책임연쇄 패턴은 각종 요청에 대해서 그것을 처리할수 있는 객체가 존재할때가지 연속적으로 객체를 탐사하며 요청을 처리할수 있는 객체를 찾아주는 패턴입니다.

 

패턴적용 스토리 -----------------------------------------------------------------------------------------------

  이번에 적용되는 스토리는 다음과 같습니다.

  당신은 퀘스트시스템을 디자인하게 되었습니다.

  퀘스트 시스템에는 엘프, 오크, 휴먼의 종족이 있고 그에 대한 퀘스트를 할당하기 위한 npc가 각기 다르게 존재합니다.

  하나의 클래스를 통해서 각 요청을 처리할수 있지만 이렇게 되면 그에 따른 분기문과 코드량이 증가할게 뻔해 보입니다.

  이런때 유용하게 사용할수 있는 것이 책임연쇄 패턴입니다.

 

uml -----------------------------------------------------------------------------------------------

  

  코드를 보면 알겠지만 데코레이터 패턴처럼 마치 리스트와 같은 구조이다.

  상위의 추상클래스를 기준으로 각 책심을 연쇄시킬 객체들을 서로 연결합니다.

  만약 하나의 연결된 객체중 하나에 요청이 들어오면 자신이 처리할수 없는 요청일 경우에는 자신과 연결된 다음 객체로 다시 요청을 보냅니다.

  그렇게 요청을 연쇄하고 요청을 처리할수 있는 객체가 등장하면 그 요청을 처리하게 됩니다.

 

 

코드 -----------------------------------------------------------------------------------------------

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
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
#include "include.h"
 
enum KIND_COUNT
{    
    KIND_MAX_COUNT = 3,
    KIND_NAME_COUNT_MAX = 20, 
};
 
class CCharacter
{
private:
    char        m_szKindName[KIND_NAME_COUNT_MAX];
    char        m_szClassName[KIND_NAME_COUNT_MAX];
 
public// constructor
    CCharacter() {}
    CCharacter(char* _pKindName, char* _pClassName)
    {
        strcpy_s(m_szKindName, _pKindName);
        strcpy_s(m_szClassName, _pClassName);
    }
    ~CCharacter() {}
 
public// Set
    void SetKind(char* _pKindName)
    { strcpy_s(m_szKindName, _pKindName); }
 
    void SetClass(char* _pClassName)
    { strcpy_s(m_szClassName, _pClassName); }
 
public// Get
    char* GetKind()
    { return m_szKindName; }
 
    char* GetClass()
    { return m_szClassName; }
};
 
class QuestNpc
{
public// member parameter
    QuestNpc*            m_pQuestNpc;
    static int            m_QuestCount;
    static CCharacter*    m_Character;
 
public// constructor
    QuestNpc()
        : m_pQuestNpc(0) { }
 
    QuestNpc(QuestNpc* _pQuestNpc)
        : m_pQuestNpc(_pQuestNpc) { }
 
public// Destructor
    virtual ~QuestNpc()
    { SAFE_DELETE(m_pQuestNpc); }
 
public:
    void SetNextQuestNpc(QuestNpc* _pQuestNpc)
    { m_pQuestNpc = _pQuestNpc; }
 
public//Quest
    virtual void QuestShow()
    {
        cout << "어느 종족에도 속해있지 않은 캐릭터입니다." << endl;
        m_QuestCount = 0;
    }
    virtual void SetPlayer(CCharacter* _Character)
    {
        m_Character = _Character;
    }
 
};
 
CCharacter* QuestNpc::m_Character = NULL;
int            QuestNpc::m_QuestCount = NULL;
 
class OrcQuestNpc : public QuestNpc
{
private : // OrcQuest
    virtual void QuestShow() 
    { 
        if( !strcmp( m_Character->GetKind(), "오크" ) )
        {
            cout << "오크 퀘스트를 배분합니다." << endl;
        }
        else
        {
            ++m_QuestCount;
            m_pQuestNpc->QuestShow();
 
            switch(m_QuestCount)
            {
            case KIND_MAX_COUNT:
                QuestNpc::QuestShow();     
                break;
            }
        }
    }
};
 
class HummunQuestNpc : public QuestNpc
{
    virtual void QuestShow() // HummunQuest
    { 
        if( !strcmp( m_Character->GetKind(), "휴먼" ) )
        {
            cout << "휴먼 퀘스트를 배분합니다." << endl;
        }
        else
        {
            ++m_QuestCount;
            m_pQuestNpc->QuestShow();
 
            switch(m_QuestCount)
            {
            case KIND_MAX_COUNT:
                QuestNpc::QuestShow();     
                break;
            }
        }
    }    
};
 
class ElfQuestNpc : public QuestNpc
{
    virtual void QuestShow() // ElfQuest
    { 
        if( !strcmp( m_Character->GetKind(), "엘프" ) )
        {
            cout << "엘프 퀘스트를 배분합니다." << endl;
        }
        else
        {
            ++m_QuestCount;
            m_pQuestNpc->QuestShow();
 
            switch(m_QuestCount)
            {
            case KIND_MAX_COUNT:
                QuestNpc::QuestShow();     
                break;
            }
        }
    }    
};
 
// Chain of Resposibility.cpp : 콘솔 응용 프로그램에 대한 진입점을 정의합니다.
//
 
#include "stdafx.h"
#include "Chain of Resposibility.h"
 
 
int _tmain(int argc, _TCHAR* argv[])
{
#if defined(DEBUG) | defined(_DEBUG)
    _CrtSetDbgFlag( _CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF );
    //_CrtSetBreakAlloc(215);
#endif
 
    CCharacter* pOrc = new CCharacter("오크""전사");
    CCharacter* pElf = new CCharacter("엘프""궁수");
    CCharacter* pHummun = new CCharacter("휴먼""마법사");
    CCharacter* pNone = new CCharacter("하플링""마법사");
    
    QuestNpc* pOrcQuestNpc = new OrcQuestNpc;
    QuestNpc* pElfQuestNpc = new HummunQuestNpc;
    QuestNpc* pHummunQuestNpc = new ElfQuestNpc;
 
    pOrcQuestNpc->SetNextQuestNpc(pElfQuestNpc);
    pElfQuestNpc->SetNextQuestNpc(pHummunQuestNpc);
    pHummunQuestNpc->SetNextQuestNpc(pOrcQuestNpc);
 
    pOrcQuestNpc->SetPlayer(pElf);
    pOrcQuestNpc->QuestShow();
    pOrcQuestNpc->SetPlayer(pOrc);
    pOrcQuestNpc->QuestShow();
    pOrcQuestNpc->SetPlayer(pHummun);
    pOrcQuestNpc->QuestShow();
    pOrcQuestNpc->SetPlayer(pNone);
    pOrcQuestNpc->QuestShow();
 
    SAFE_DELETE(pOrc);
    SAFE_DELETE(pElf);
    SAFE_DELETE(pHummun);
    SAFE_DELETE(pNone);
 
    SAFE_DELETE(pOrcQuestNpc);
    SAFE_DELETE(pElfQuestNpc);
    SAFE_DELETE(pHummunQuestNpc);
 
    return 0;
}

 

결과 ----------------------------------------------------------------------------------------------- 

 

 

Posted by JJOREG

프록시 패턴 --------------------------------------------------------------------------------------------------

  하나의 객체는 내부적으로 다양한 맴버를 가지고 혹은 포인터를 가질때도 많습니다.

  혹은 하나의 거대한 이미지나 데이터를 사용하기 용이하게 하기 위한 객체로 디자인 되기도 합니다.

  하지만 실제 게임이나 프로그램을 보면 한 유저가 게임을 실행하고 게임내의 모든 데이터를 사용하는 경우는 거의 없습니다.

  온 지역을 돌아다니며 모든 몬스터를 만다고 모든 아이템을 한꺼번에 모두 가지고 있는 경우는 희귀한 편이고

  게임 디자인또한 그런식으로 되지 않습니다.

  대리자 패턴은 그런 경우 꼭 필요한 객체만을 생성하고 그 함수를 이용할수 있게 해주거나.

  가벼운 일은 가벼운 객체만 생성해서 해결하고 실제 아직 요청되지 않은 무거운 객체는 나중에 생성하게 하는 패턴입니다.

 

l        Remote Proxy로서의 역할. 원격의 객체에 대한 로컬의 대리자(local representative)를 제공하는데 쓰인다.
l        Virtual Proxy로서의 역할. 많은 비용이 요구되는 객체를 생성하는 경우에 사용된다. 앞서 컨텍스트 항목에서 설명한 경우가 이러한 적용의 예다.
l        Protection Proxy로서의 역할. 보호가 요구되는 객체 자체에 대한 접근을 통제하고 대리자를 통해 접근 가능한 정도만 노출시키고자 할 때 쓴다.
l        Smart Reference로서의 역할. 객체에 대한 단순한 접근 이외의 부가적인 작업을 수행할 필요가 있을 때 사용한다.

 

패턴적용 스토리 -----------------------------------------------------------------------------------------------

  이번에 적용되는 스토리는 다음과 같습니다.

  당신은 아이템 클래스의 정보와 랜더를 맡게 되었습니다.

  아이템의 기능은 크게 2가지 입니다.

  인벤창에 있을때 아이템에 대한 데이터를 보여준다.

  장착 했을때 캐릭터에게 3dmash 이미지를 띄워준다.

  아이템 인포는 매우 작은 작업니다.

  하지만 3가지 분류의 1000가지가 넘는 아이템이 있고 실제 그 아이템을 플레이어가 모두 소유하고 있지는 않기 때문에 필요할때만

  매쉬를 생성하고 마찬가지로 필요할 때만 아이템에 대한 정보를 로드하기로 했습니다.

 

uml -----------------------------------------------------------------------------------------------

 

 

보시면 알겠지만 플라이웨이트 패턴과 결합되어 있는 구조입니다.

대리 객체인 CItemProxy 클래스는 내부에 자신과 같이 CitemBase를 상속받고있는 CItem 클래스를 보유하고 있습니다.

내부적으로 3가지 아이템의 생성에 대해서 관여하고 있으며 각 플라이웨이트패턴의 캐쉬들에 대해서 직접적인 요청을 하는것은 아이템 프록시 패턴입니다.

일정부분 패턴을 나갔기 때문에 여러가지 패턴이 섞여있고 응용성을 높여 봤으니 구조를 잘파악하면서 확인해 봐야 합니다.

 

하지만 중요한 것은 하나의 가상클래스를 상속받는 프록시클래스와 실제 동작을 담당하는 리얼클래스가 존재하며

프록시 클래스는 리얼클래스를 내부에 포함한 상태로 그 객체를 통해서 실제 동작을 정의한다는 것이 중요한 것입니다.

또한 플라이 웨이트 패턴에서는 기존의 객체의 로딩을 최소화 하는데만 그치지 않고

내부적으로는 아무런 객체도 가지고 있지 않다고 요청이 들어왔을때 존재하면 포인터를 리턴해주고

존재하지 않는다면 내부적으로 파일을 탐색하여 그때그때 진정으로 필요한 객체만 생성하는 방식을 사용하고 있으니 그점에 대해서도 잘 확인해야 합니다.

 

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
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
#pragma once
 
#include "include.h"
 
const int MaxItemName = 100;
 
const int ItemDataIndexMax = 10;
const int MashIndexMax = 10;
 
enum ITEMTYPE
{
    ITEMTYPE_ATT,
    ITEMTYPE_DEF,
    ITEMTYPE_ACC,
};
 
typedef struct Iteminfo
{
    char szItemName[MaxItemName];
    int iItemRenderIndex;
    int iItemType;
    int iItemOption;
 
    Iteminfo() {}
 
    Iteminfo(const char* _szItemName, const int& _iItemRenderIndex, const int& _ItemType, const int& _iItemOption)
    : iItemRenderIndex(_iItemRenderIndex) 
    , iItemType(_ItemType)
    , iItemOption(_iItemOption)
    { 
        strcpy_s(szItemName, _szItemName);
    }
 
}ITEMINFO, *PITEMINFO;
 
class Item3DMash
{
private:
    int MashIndex;
 
public:
    const int& GetMashIndex(void) {return MashIndex; }
 
public:
    void MashRender()
    { cout << MashIndex << "번 인덱스 매쉬 랜더" << endl; }
 
 
public:
    Item3DMash(const int _MashIndex) : MashIndex(_MashIndex) { cout << "매쉬 생성 완료" << endl; }
    ~Item3DMash() {}
};
 
 
Item3DMash* FindItemMashFile(const int& iKey)
{
    Item3DMash* pMash;
 
    if(iKey >= MashIndexMax)
    {
        return NULL;
    }
 
    pMash = new Item3DMash(iKey);
 
    return pMash;
}
 
class CFlyItemMash
{
private:
    map<int, Item3DMash*>    m_ProtoMash;
 
public:
    Item3DMash* FindMash(const int& iKey)
    {
        map<int, Item3DMash*>::iterator ItemMashIter = m_ProtoMash.find(iKey);
 
        if(ItemMashIter == m_ProtoMash.end())
        {
            return NULL;
        }
 
        return ItemMashIter->second;
    }
 
    bool MashInsert(Item3DMash* _pMash)
    {
        if(!_pMash)
        { 
            cout << "매쉬 데이터 정보 오류." << endl;
            return false;
        }
 
        map<int, Item3DMash*>::iterator ItemMashIter = m_ProtoMash.find(_pMash->GetMashIndex());
 
        if(ItemMashIter != m_ProtoMash.end())
        { 
            cout << "이미 로딩한 매쉬입니다." << endl;
            return false;
        }
 
        m_ProtoMash.insert(map<int, Item3DMash*>::value_type(_pMash->GetMashIndex(), _pMash));
        return true;
    }
 
    Item3DMash* GetMash(const int& iKey)
    {
        Item3DMash* pMash = FindMash(iKey);
 
        if(!pMash)
        {
            pMash = FindItemMashFile(iKey);
 
            if(MashInsert(pMash))
            { return pMash; }
            else
            { return NULL; }
        }
 
        return pMash;
    }
 
private:
    static CFlyItemMash* m_Inst;
 
public:
    static CFlyItemMash* GetInst()
    {
        if(m_Inst == NULL)
        { m_Inst = new CFlyItemMash(); }
        return m_Inst;
    }
 
    void DestroyInst() { SAFE_DELETE(m_Inst); }
 
 
private:
    CFlyItemMash(void) {}
public:
    ~CFlyItemMash(void) {SAFE_DELETE_MAP(m_ProtoMash);}
};
 
CFlyItemMash* CFlyItemMash::m_Inst = NULL;
 
PITEMINFO FindItemDataFile(const int& iKey)
{
    PITEMINFO pInfo = NULL;
 
    if(iKey >= ItemDataIndexMax)
    {
        cout << iKey << "키의 아이템정보 파일이 존재하지 않습니다." << endl;
        return NULL;
    }
 
    char szName[MaxItemName];
 
    sprintf_s(szName, "%dITEM", rand() % 10);
 
    pInfo = new Iteminfo(szName, rand() % 10, rand() % 10, rand() % 100);
 
    return pInfo;
}
 
class CFlyItemDataBase
{
private:
    map<int, PITEMINFO>    m_ProtoSkillData;
 
public:
 
    ITEMINFO* FindItemInfo(const int& iKey)
    {
        map<int, PITEMINFO>::iterator ItemInfoIter = m_ProtoSkillData.find(iKey);
 
        if(ItemInfoIter == m_ProtoSkillData.end())
        {
            return NULL;
        }
 
        return ItemInfoIter->second;
    }
 
    bool SkillDataInsert(PITEMINFO _Iteminfo)
    { 
        if(!_Iteminfo)
        { 
            cout << "아이템 데이터 정보 오류." << endl;
            return false;
        }
 
        map<int, PITEMINFO>::iterator ItemInfoIter = m_ProtoSkillData.find(_Iteminfo->iItemRenderIndex);
 
        if(ItemInfoIter != m_ProtoSkillData.end())
        { 
            cout << "입력하려는 아이템 정보가 이미 존재합니다." << endl;
            return false;
        }
 
        m_ProtoSkillData.insert(map<int, PITEMINFO>::value_type(_Iteminfo->iItemRenderIndex, _Iteminfo));
 
        return true;
    }
 
    const PITEMINFO GetSkillInfo(const int& iKey)
    {
        PITEMINFO pInfo = FindItemInfo(iKey);
 
        if(!pInfo)
        {
            pInfo = FindItemDataFile(iKey);
 
            if(SkillDataInsert(pInfo))
            { return pInfo; }
            else
            { return NULL; }
        }
 
        return pInfo;
    }
 
private:
    static CFlyItemDataBase* m_Inst;
 
public:
    static CFlyItemDataBase* GetInst()
    {
        if(m_Inst == NULL)
        { m_Inst = new CFlyItemDataBase(); }
        return m_Inst;
    }
 
    void DestroyInst() { SAFE_DELETE(m_Inst); }
 
private:
    CFlyItemDataBase(void) {}
public:
    virtual ~CFlyItemDataBase(void) {}
 
};
 
CFlyItemDataBase* CFlyItemDataBase::m_Inst = NULL;
 
class CItemBase
{
public:
    virtual void SkillInfoSetting(const ITEMTYPE& iType, const int& iKey) = 0;
    virtual void SKillInfoRender() = 0;
    virtual void ItemMashRender() = 0;
 
public:
    CItemBase() {}
    virtual ~CItemBase() {}
};
 
class CItem
    : public CItemBase
{
protected:
    Item3DMash*        m_pItemMash;
    PITEMINFO        m_pItemInfo;
 
public:
    virtual void SkillInfoSetting(const ITEMTYPE& iType, const int& iKey)
    { 
        m_pItemMash = CFlyItemMash::GetInst()->GetMash(iKey);
        m_pItemInfo = CFlyItemDataBase::GetInst()->GetSkillInfo(iKey);
    }
    virtual void SKillInfoRender()
    { 
        cout << "**** 아이템 정보 ****" << endl; 
    }
    virtual void ItemMashRender()
    { cout << "**** 아이템 매쉬랜더 ****" << endl; }
    
public:
    void SetItemInfo(const PITEMINFO& _pItemInfo)
    {
        m_pItemInfo = _pItemInfo;
    }
 
    void SetMash(Item3DMash* _pItemMash)
    {
        m_pItemMash = _pItemMash;
    }
 
public:
    CItem() : m_pItemMash(NULL), m_pItemInfo(NULL) {}
    virtual ~CItem() {}
};
 
class CAttItem
    : public CItem
{
public:
    virtual void SkillInfoSetting(const ITEMTYPE& iType, const int& iKey)
    {
        CItem::SkillInfoSetting(iType, iKey);
    }
 
    virtual void SKillInfoRender()
    {
        CItem::SKillInfoRender();
        if(m_pItemInfo)
        {
        cout << "무기 정보" << endl;
        cout << "이름" << m_pItemInfo->szItemName << " "  ;
        cout << "타입" << m_pItemInfo->iItemType << " " ;
        cout << "수치" << m_pItemInfo->iItemOption << endl;
        }
    }
 
    virtual void ItemMashRender()
    {
        CItem::ItemMashRender();
        cout << "무기 랜더 위치 설정" << endl;
        if(m_pItemMash)
            m_pItemMash->MashRender();
 
        cout << endl;
    }
 
public:
    CAttItem() {}
    virtual ~CAttItem() {}
};
 
class CDefItem
    : public CItem
{
public:
    virtual void SkillInfoSetting(const ITEMTYPE& iType, const int& iKey)
    {
        CItem::SkillInfoSetting(iType, iKey);
    }
 
    virtual void SKillInfoRender()
    {
        CItem::SKillInfoRender();
        if(m_pItemInfo)
        {
        cout << "방어구 정보" << endl;
        cout << "이름" << m_pItemInfo->szItemName << " "  ;
        cout << "타입" << m_pItemInfo->iItemType << " " ;
        cout << "수치" << m_pItemInfo->iItemOption << endl;
        }
    }
 
    virtual void ItemMashRender()
    {
        CItem::ItemMashRender();
        cout << "방어구 랜더 위치 설정" << endl;
        if(m_pItemMash)
        m_pItemMash->MashRender();
 
        cout << endl;
    }
 
public:
    CDefItem() {}
    virtual ~CDefItem() {}
};
 
class CAccItem
    : public CItem
{
public:
    virtual void SkillInfoSetting(const ITEMTYPE& iType, const int& iKey)
    {
        CItem::SkillInfoSetting(iType, iKey);
    }
 
    virtual void SKillInfoRender()
    {
        CItem::SKillInfoRender();
        if(m_pItemInfo)
        {
        cout << "방어구 정보" << endl;
        cout << "이름" << m_pItemInfo->szItemName << " "  ;
        cout << "타입" << m_pItemInfo->iItemType << " " ;
        cout << "수치" << m_pItemInfo->iItemOption << endl;
        }
    }
 
    virtual void ItemMashRender()
    {
        CItem::ItemMashRender();
        cout << "방어구 랜더 위치 설정" << endl;
        if(m_pItemMash)
            m_pItemMash->MashRender();
 
        cout << endl;
    }
 
public:
    CAccItem() {}
    virtual ~CAccItem() {}
};
 
class CItemProxy
    : public CItemBase
{
private:
    CItemBase*            m_Item;
 
public:
    virtual void SkillInfoSetting(const ITEMTYPE& iType, const int& iKey)
    {
        if(m_Item == NULL)
        {
            switch(iType)
            {
            case ITEMTYPE_ATT :
                m_Item = new CAttItem();
                break;
            case ITEMTYPE_DEF :
                m_Item = new CDefItem();
                break;
            case ITEMTYPE_ACC :
                m_Item = new CAccItem();
                break;
            }
        }
        m_Item->SkillInfoSetting(iType, iKey);
    }
 
    virtual void SKillInfoRender()
    {
        m_Item->SKillInfoRender();
    }
 
    virtual void ItemMashRender()
    {    
        m_Item->ItemMashRender();
    }
 
public:
    CItemProxy() : m_Item(NULL) {}
    virtual ~CItemProxy(void) {}
};
 
// Proxy.cpp : 콘솔 응용 프로그램에 대한 진입점을 정의합니다.
//
 
#include "stdafx.h"
#include "Proxy.h"
 
 
int _tmain(int argc, _TCHAR* argv[])
{
 
    CItemProxy* pProxyAtt = new CItemProxy;
 
    pProxyAtt->SkillInfoSetting(ITEMTYPE_ATT, 2);
 
    pProxyAtt->SKillInfoRender();
 
    pProxyAtt->ItemMashRender();
 
    CItemProxy* pProxyDef = new CItemProxy;
 
    pProxyDef->SkillInfoSetting(ITEMTYPE_DEF, 8);
 
    pProxyDef->SKillInfoRender();
 
    pProxyDef->ItemMashRender();
 
    CItemProxy* pProxyAcc = new CItemProxy;
 
    pProxyAcc->SkillInfoSetting(ITEMTYPE_ACC, 10);
 
    pProxyAcc->SKillInfoRender();
 
    pProxyAcc->ItemMashRender();
 
    SAFE_DELETE(pProxyAtt);
    SAFE_DELETE(pProxyDef);
    SAFE_DELETE(pProxyAcc);
 
    return 0;
}

 

 

Posted by JJOREG

플라이 웨이트 패턴 --------------------------------------------------------------------------------------------------

  일반적으로 포인터나 주소값은 대부분 같은 것을 참조할 때가 많습니다.

  하지만 대부분의 객체가 이러한 포인터 값을 다수 가지고 있다면 메모리에 대한 부담이 커질수가 있습니다.

  그래서 하나의 자료구조에 포인터를 모아놓고 사용해야할 객체들이 그 정보들을 전역변수처럼 돌려쓰는 것이

  대표적인 것이 이미지에 대한 정보 입니다. 완전히 같은 이미지를 사용하는 2개의 객체가 있을때.

  굳이 2개의 이미지를 로딩하는 것이 아니라. 1개만 로딩한후 그것을 돌려쓰면 됩니다.


 

패턴적용 스토리 -----------------------------------------------------------------------------------------------

  당신에게 오브젝트 랜더에에 대한 임무가 내려졌습니다.

  오브젝트 랜더를 위해서는 이미지가 필요한데.

  각 오브젝트마다 이미지를 로딩해 봤더니 이미지에 대한 데이터부담이 너무 커졌습니다.

  그래서 당신은 플라이웨이트 패턴을 사용하여 만약 오브젝트들이 같은 이미지를 랜더한다면 그 이미지를 한번만 로딩하기로 결정했습니다.

 

uml -----------------------------------------------------------------------------------------------

 

 

플라이 웨이트패턴을 보면 오브젝트에서는 CFlyTexture에 있는 stl자료구조 안에 담긴 텍스처의 포인터를 가져와서 랜더를 합니다.

즉 프로토타입처럼 하나의 이미지를 다수의 오브젝트가 공유하여 사용하는 패턴입니다.

 

코드 -----------------------------------------------------------------------------------------------

 

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
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
#pragma once
#include "include.h"
 
enum TEXTYPE
{
    TEXTYPE_SINGLE,
    TEXTYPE_MULTI,
};
 
enum FILENAME_COUNT
{
    FILENAME_COUNT_MAX = 100,
};
 
class Texture
{
protected:
    char    szName[FILENAME_COUNT_MAX];
 
public:
    virtual void LoadTexture(const char* pfilename) = 0;
    virtual void GetTexture(const int _iKey, const int TexCount = 0) = 0;
    virtual void Render(void) = 0;
};
 
class SingleTexture
    : public Texture
{
public:
    virtual void LoadTexture(const char* pfilename)
    {
        cout << pfilename << "텍스처 로딩" <<endl;
        strcpy_s(szName, pfilename);
    }
    virtual void GetTexture(const int _iKey, const int _TexCount = 0)
    {
        cout << szName << "텍스처 사용" <<endl;
    }
    virtual void Render(void)
    {
        cout << szName << "랜더" <<endl;
    }
 
public:
    SingleTexture(void) {}
    virtual ~SingleTexture(void) {}
};
 
class MultiTexture
    : public Texture
{
public:
    virtual void LoadTexture(const char* _pfilename)
    {
        cout << _pfilename << "폴더에 있는 모든 텍스처 로딩" <<endl;
        strcpy_s(szName, _pfilename);
    }
    virtual void GetTexture(const int _iKey, const int _TexCount)
    {
        cout << szName << " " << _TexCount << "텍스처 사용" <<endl;
    }
    virtual void Render(void)
    {
        cout << szName << " " << "애니메이션 랜더" <<endl;
    }
 
public:
    MultiTexture(void) {}
    virtual ~MultiTexture(void) {}
};
 
class CFlyTexture
{
private:
    map<const int, Texture*> m_mapTexture;
 
public:
    bool InsertTexture(const char* pfilename, const int iKey, const TEXTYPE& _eTexType)
    {
        map<const int, Texture*>::iterator Iter = m_mapTexture.find(iKey);
 
        if(Iter != m_mapTexture.end())
        { cout << "이미 추가된 텍스처 입니다" << endl; return true; }
 
        Texture* pTex;
 
        switch(_eTexType)
        {
        case TEXTYPE_SINGLE:
            {
                pTex = new SingleTexture;
                pTex->LoadTexture(pfilename);
                break;
            }
 
        case TEXTYPE_MULTI:
            {
                pTex = new MultiTexture;
                pTex->LoadTexture(pfilename);
                break;
            }
        }
 
        m_mapTexture.insert( map<const int, Texture*>::value_type(iKey, pTex) );
 
        return false;
    }
 
    void Init(void)
    {
        InsertTexture("지형지물0", 0, TEXTYPE_SINGLE);
        InsertTexture("지형지물1", 1, TEXTYPE_SINGLE);
        InsertTexture("지형지물2", 2, TEXTYPE_SINGLE);
        InsertTexture("지형지물3", 3, TEXTYPE_SINGLE);
        InsertTexture("몬스터1",  4, TEXTYPE_MULTI);
        InsertTexture("몬스터2",  5, TEXTYPE_MULTI);
        InsertTexture("플레이어", 6, TEXTYPE_MULTI);
 
        cout << endl;
    }
 
    Texture* GetTexture(const int iKey, const int TexCount = 0)
    {
        map<const int, Texture*>::iterator Iter = m_mapTexture.find(iKey);
 
        if(Iter == m_mapTexture.end())
        {
            cout << "텍스처가 없습니다" << endl;
            return NULL;
        }
 
        return Iter->second;
    }
 
    void Release(void)
    {
        map<const int, Texture*>::iterator Iter;
        for(Iter = m_mapTexture.begin(); Iter != m_mapTexture.end() ; ++Iter)
        {
            SAFE_DELETE(Iter->second);
        }
        m_mapTexture.clear();
    }
 
public:
    static CFlyTexture* m_Inst;
 
    static inline CFlyTexture* GetInst(void)
    {
        if(m_Inst == NULL)
            m_Inst = new CFlyTexture();
        return m_Inst;
    }
 
    static void DestroyInst(void)
    {
        SAFE_DELETE(m_Inst);
    }
 
private:
    CFlyTexture(void) {}
public:
    ~CFlyTexture(void) { Release(); }
};
 
CFlyTexture* CFlyTexture::m_Inst = NULL;
 
class CObject
{
private:
    int        TexNum;
 
public:
    void Render(void)
    {
        Texture* pTex = CFlyTexture::GetInst()->GetTexture(TexNum);
 
        if(pTex)
            pTex->Render();
    }
 
public:
    CObject(int _Tex) {TexNum = _Tex;}
    ~CObject() {}
};
 
// Flyweight.cpp : 콘솔 응용 프로그램에 대한 진입점을 정의합니다.
//
 
#include "stdafx.h"
#include "Flyweight.h"
 
 
int _tmain(int argc, _TCHAR* argv[])
{
    CFlyTexture::GetInst()->Init();
 
    CObject* pTerrain0 = new CObject(0);
    CObject* pTerrain1 = new CObject(0);
    CObject* pTerrain2 = new CObject(1);
    CObject* pTerrain3 = new CObject(1);
    CObject* pMonster1 = new CObject(5);
    CObject* pMonster2 = new CObject(5);
    CObject* pMonster3 = new CObject(6);
    CObject* pPlayer   = new CObject(6);
 
    pTerrain0->Render();
    pTerrain1->Render();
    pTerrain2->Render();
    pTerrain3->Render();
    pMonster1->Render();
    pMonster2->Render();
    pMonster3->Render();
    pPlayer->Render();
 
    SAFE_DELETE(pTerrain0);
    SAFE_DELETE(pTerrain1);
    SAFE_DELETE(pTerrain2);
    SAFE_DELETE(pTerrain3);
    SAFE_DELETE(pMonster1);
    SAFE_DELETE(pMonster2);
    SAFE_DELETE(pMonster3);
    SAFE_DELETE(pPlayer);
 
    CFlyTexture::DestroyInst();
 
    return 0;
}

결과 -----------------------------------------------------------------------------------------------

 

Posted by JJOREG

시간을 알려주는 구조체이다. 사용법은 아래와 같고.

자세한 내용은 msdn의 구조체 항목에서 datetime 구조체를 참고하면 된다.


using System;

using System.Collections.Generic;

using System.Linq;

using System.Text;


namespace ConsoleApplication1

{

    class Program

    {


        static void Main(string[] args)

        {

            DateTime dt = DateTime.Now;


            string str;


            str = string.Format("금일은 {0}년 {1}월 {2}일({3})이다", dt.Year, dt.Month, dt.Day, dt.DayOfWeek);

            Console.WriteLine(str);

            str = string.Format("현재 {0}시 {1}분 {2}초({3})이다", dt.Hour, dt.Minute, dt.Second, dt.Millisecond);

            Console.WriteLine(str);

            str = string.Format("dt.Date {0}", dt.Date);

            Console.WriteLine(str);

            str = string.Format("짧은 날짜 형식 {0}", dt.ToShortDateString());

            Console.WriteLine(str);

        }

    }

}

Posted by JJOREG

namespace ConsoleApplication1

{

    struct MyData

    {

        public int x, y;


        public MyData(int x, int y)

        {

            this.x = x;

            this.y = y;

        }


        public MyData(ref int x, ref int y)

        {

            this.x = 0;

            this.y = 0;

        }


        public void NoShow()

        {

            Console.WriteLine("no swap");

            Console.WriteLine("{0}        {1}", this.x, this.y);

        }


        public void Show()

        {

            Console.WriteLine("swap");

            Console.WriteLine("{0}        {1}", this.x, this.y);

        }

    }


    class MyDataClass

    {

        public int x = 10, y = 100;


        public void NoShow()

        {

            Console.WriteLine("No swap");

            Console.WriteLine("{0}        {1}", x, y);

        }


        public void Show()

        {

            Console.WriteLine("swap");

            Console.WriteLine("{0}        {1}", x, y);

        }

    }


    class Swap

    {


        static public void CSwapdata(MyDataClass md)

        {

            int temp;

            temp = md.x;

            md.x = md.y;

            md.y = temp;

        }


        static public void CSwapdata1(ref MyDataClass md)

        {

            int temp;

            temp = md.x;

            md.x = md.y;

            md.y = temp;

        }


        static public void Swapdata(MyData md)

        {

            int temp;

            temp = md.x;

            md.x = md.y;

            md.y = temp;

        }


        static public void Swapdata1(ref MyData md)

        {

            int temp;

            temp = md.x;

            md.x = md.y;

            md.y = temp;

        }

    }


    class Program

    {


        static void Main(string[] args)

        {

            MyData Md = new MyData(10, 100);


            MyData Md1;

            Md1.x = 10;

            Md1.y = 100;


            Console.WriteLine("new struct value");


            Md.NoShow();


            Swap.Swapdata(Md);


            Md.Show();


            Console.WriteLine("new struct ref");


            Md.NoShow();


            Swap.Swapdata1(ref Md);


            Md.Show();


            Console.WriteLine("value struct value");


            Md1.NoShow();


            Swap.Swapdata(Md1);


            Md1.Show();


            Console.WriteLine("value struct ref");


            Md1.NoShow();


            Swap.Swapdata1(ref Md1);


            Md1.Show();


            MyDataClass CMd = new MyDataClass();


            Console.WriteLine("class value swap");


            CMd.NoShow();


            Swap.CSwapdata(CMd);


            CMd.Show();


            Console.WriteLine("class ref swap");


            CMd.NoShow();


            Swap.CSwapdata1(ref CMd);


            CMd.Show();


        }

    }

}


구문이 좀 길지만 실행해보면 차이를 알수 있다.


구조체의 경우 ref로 참조하지 않을 경우 원본의 데이터를 바꿀수 없다.

이것은 구조체를 new로 할당하거나 그냥 할당해도 마찬가지이다.


클래스의 경우에는 반대이다. 어떤 방식으로 넣어도(ref로 넣던 값형으로 넣던) 내부의 데이터를 변경할수가 있다.


즉 클래스는 애초부터 참조형 형태의 데이터이고 구조체의 경우에는 값형 데이터이며 이 차이를 명확하게 인식해야 한다.

'게임개발공부 > C#공부' 카테고리의 다른 글

C# 프로그래밍 <윈폼> <이벤트> <델리게이트>  (0) 2014.01.10
DateTime 구조체  (0) 2013.12.30
변수가 사용됐다? 초기화의 순서는?  (0) 2013.12.30
인덱서  (0) 2013.12.30
구조체  (0) 2013.12.30
Posted by JJOREG

빠르게 배워나가는 도중이기 때문에 변수나 함수에 대해서 깊게 파고 있지는 않았다.

하지만 C#에서는 몇가지 새로운 경고와 변수의 사용시점등이 약간 다른 면이 있어서 다음과 같이 정리해 보았다.


    class aaa

    {

        private int c = 0; -> 선언 순간 초기화. 봐도 신기하기는 하다.


        public void a()

        {

            this.c = 1;

        }

    }


위의 클래스를 컴파일 하면 다음과 같은 경고가 뜬다.

경고 CS0414: 'ConsoleApplication1.aaa.c' 필드가 할당되었지만 그 값은 사용되지 않습니다.

사용되지 않았다? 그냥 상수 값의 대입은 컴파일러가 사용하지 않았다라고 판단하는 것 같다.

그럼 어떤때 사용했다고 할까?



    class aaa

    {

        private int c = 100;


        public void a()

        {

            int dd = 100;

            this.c = dd; -> 이 녀석을 대입하면 사용했다고 본다. 상수가 지역변수등을 혹은 public void a(int temp) { this.c = temp;} 같은 식도 된다.

            b(this.c); -> 아무것도 없는 함수지만 내부에 넣었다는 것만으로도 사용한 것으로 본다.

        }


        public void b(int a)

        {

        }


        public void p()

        {

            Console.WriteLine(c); -> 마찬가지다.

        }


        public aaa(int a)

        {

            this.c = a; -> 생성자를 만들어서 대입해주면 역시나 사용했다고 인식한다.

        }


    }

'게임개발공부 > C#공부' 카테고리의 다른 글

DateTime 구조체  (0) 2013.12.30
클래스와 구조체, 참조형과, 값형  (0) 2013.12.30
인덱서  (0) 2013.12.30
구조체  (0) 2013.12.30
추상 클래스와 인터페이스의 차이란?  (0) 2013.12.29
Posted by JJOREG

인덱서란? ------------------------------------------------------------------------------------------------------------------------


  인덱서란 내부에 존재하는 맴버변수나 혹은 인스턴스를 배열과 같이 취급하는 것을 인덱서라고 한다.

  문법적으로는 C#의 속성의 배열 버전이라고 보면 된다 보인다.


    class SampleCollection<T>

    {

        private T[] arr = new T[100]; -> 어라 클래스의 맴버변수를 내부에서 그냥 초기화 해버린다. 가능했구나...

  private int c = 0; -> 이렇게 굳이 할당을 하지 않아도 가능하다.

        public T this[int i] -> 이녀석이 인덱서이다. this구문을 무조건 붙여줘야 한다.

        {

            get

            {

                return arr[i]; -> 내부 인스턴스의 값을 배열적인 측면으로 접근이 가능하다.

            }

            set

            {

                arr[i] = value; -> 마찬가지로 대입도 가능하다.

            }

        }

    }


    // This class shows how client code uses the indexer

    class Program

    {

        static void Main(string[] args)

        {

            SampleCollection<string> stringCollection = new SampleCollection<string>();

            stringCollection[0] = "Hello, World"; -> 객체에서 즉각 접근이 가능해진다.

            stringCollection[1] = "World, Hello"; -> 배열이니 인덱스를 바꾸는 것도 마찬가지.

            System.Console.WriteLine(stringCollection[1]);

        }

    }

Posted by JJOREG
이전버튼 1 2 3 4 5 6 7 ··· 13 이전버튼