예전것/프로그래밍2010. 1. 13. 21:33
http://withrobot.tistory.com/180
위 사이트에 나와있다.

<textarea name="code" class="Python" cols="60" rows="10">
코드는 여기에 복사한다.
</textarea>
예전것/프로그래밍2009. 12. 8. 23:01
예전것/프로그래밍2009. 10. 16. 18:22

virtual은 3가지 정도로 사용됩니다. OOP에 대한 개념이 잡혀있어야 설명을 제대로 이해하실 수 있을텐데.. 최대한 쉽게 설명 해보도록 하죠. 

일단 OOP 개념 중 클래스 상속에 대한 것은 잘 아실겁니다. 이마저도 모르신다면 virtual에 대해서는 전혀 궁금해하실 필요가 없으니 그냥 넘어가시면 되구요.. 진도를 앞서나가는 호기심은 해롭습니다! :-) 

class Bird {

    // 중략

}; 

class Chicken : public Bird {

    // 중략

}; 

class Eagle : public Bird {

    // 중략

};

 

와 같은 구조가 있다고 합시다.

C++에서 public 상속은 is-a 관계로 설명할 수 있는데 쉽게 말하자면 Chicken is a Bird. 입니다. 

따라서 Bird *pBird = new Chicken; 과 같이 Chicken 인스턴스(클래스가 실체화된 것)를 Bird 객체에 대입할 수 있습니다.  이를 다형성(polymorphism)이라고 하고요. 

이런 지식을 깔아놓고 시작하죠.

1. 순수 추상(pure abstract, 혹은 순수 가상pure virtual) 함수 선언으로서의 virtual

순수 가상 함수란 함수 바디를 가지지 않은 함수를 말하며 그러한 함수를 가지고 있는 클래스를 추상 클래스(abstract class)라고 합니다. 

그럼 abstact 시리즈는 어디서 쓰는가? 에 대한 예를 하나 들어보죠. 

모든 Bird를 상속받은 객체는 bool canFly(); 란 멤버함수를 가지고 있다고 합시다. 이름만 봐도 알겠지만, 날 수 있으면 true, 못날면 false를 리턴합니다. 

그럼 canFly() 멤버함수는 Chicken에서도 쓸 수 있어야 하고 Eagle 객체에서도 쓸 수 있어야 합니다. 하지만 이걸 Bird 클래스에 정의할 수는 없습니다. 당연하잖아요? Chicken은 false고 Eagle은 true인데요. 아, 여기서 Overriding을 생각하셨다면 아주 깊은 이야기까지 꺼내야 해서 좀 난감해지는데요. 순수 가상 함수를 정의하는 것과 단순히 Overriding하는 것은 객체 지향 설계상의 미묘한 차이가 있고, 뒤에서 설명할 virtual의 쓰임새 2.와도 관련이 있는데, 그냥 간단하게 생각해서 지금 이 경우는 Bird 객체에서 canFly() 함수가 할 일은 없으므로 순수 가상 함수로 선언하는 것이 맞습니다.  

또 한가지 더, 그렇다면 그냥  Bird 클래스에 canFly()를 선언할 필요없이 Chicken 클래스랑 Eagle 클래스에 따로 만들어주면 될 것 아닌가? 라는 생각을 하실 수도 있습니다. 이렇게 되면 다형성을 이용해 Bird 객체에 Chicken이나 Eagle 객체를 담았을 때 canFly() 함수를 호출할 수 없게 됩니다. 즉, 다음의 코드가 불가능합니다. 

Bird* pBird = new Chicken();

pBird->canFly(); // Bird 클래스 선언에는 canFly() 함수가 없습니다! 

따라서 반드시 Bird 클래스는 canFly()에 대한 정의를 가져야 합니다. (사실 여기에도 다른 문제가 존재하는데, 이건 virtual 사용 용도 2. 번에서 살펴봅니다.) 

즉, Bird를 상속받는 모든 클래스들은 canFly() 함수를 구현할 의무를 지닌다.. 정도가 되겠습니다. 물론 구현 안할 수도 있습니다. 그렇다면 그 클래스도 결국 추상 클래스가 되죠. 어쨌든, 이런 함수가 필요는 한데, 실제 구현은 자신을 상속받는 클래스들에게로 미루는 것이 바로 순수 가상 함수입니다. 선언은 아래와 같이 됩니다.

 

class Bird {

    public:

        virtual bool canFly() = 0;

        // 이렇게 하면 순수 가상 함수로 선언되고

        // Bird는 인스턴스화가 불가능한 추상 클래스가 됩니다.  

};

 

class Chicken : public Bird {

    public:
        // 여기서 실제로 구현해줍니다.
        bool canFly()
        {
             return false;
         }
};

 

class Eagle : public Bird {

   public:
        // 여기서 실제로 구현해줍니다.
        bool canFly()
        {
             return true;
         }
};

 

이렇게 선언해놓으면, 다형성을 이용하여 Bird 객체(라는 용어가 맞지는 않습니다. 실제로 생성되는건 Bird 객체가 아니라 Chicken이나 Eagle 객체이기 때문입니다. 하지만 그냥 이렇게 이야기하도록 하죠) 에서 canFly() 함수를 호출하게 되는겁니다. 물론, 실제 호출되는 함수는 Bird 객체가 실제로 어떤 객체를 담고 있었느냐에 따라 C++이 자동으로 호출하게 됩니다. Bird에서 canFly()는 순수 가상 함수이기 때문에 자동으로 2. 에서 설명하는 동적 바인딩을 사용하게 됩니다.

 

그리고 앞서도 말했지만 순수 가상 함수를 가진 추상 클래스인 Bird는 실제로 객체를 생성(인스턴스화)할 수 없고 오직 포인터나 레퍼런스등의 용도로만 사용할 수 있습니다. 이게 무슨 필요냐.. 하실 분도 계시겠지만, 객체 지향 개념에서 가장 요긴하고 중요한 개념 중 하나입니다. 내공이 깊어지면 이해되실겁니다. 

2. 상속, Overriding과 관련하여 동적 바인딩을 지시하기 위한 virtual

C++은 기본적으로 함수 호출에 정적 바인딩을 사용합니다. JAVA는 기본이 동적 바인딩이죠. 그렇다면 정적 바인딩과 동적 바인딩은 어떤 것인가? 에 대해 알아봅시다.

 예를 들어 우리가 아래와 같은 함수 호출을 한다고 합시다.

 

void doSomething(int a, int b)
{
    printf("%d, %d\n", a, b);

이 경우 컴파일러가 컴파일(일반적으로 말하듯이, 링킹 과정도 포함하는 것으로 합시다. 정확한 용어는 빌드죠)할 때 printf가 무엇인지는 명확합니다. 즉, 프로그램이 실행되면서 저 코드에서 호출하는 printf() 함수가 다른 것으로 바뀔 리는 없다는거죠. 

 

class Bird {

    public:

        string getName { return string("Bird"); }

};

 

class Chicken : public Bird {

    public:

        string getName { return string("Chicken"); }

};

 

class Eagle : public Bird {

   public:

        string getName { return string("Eagle"); }

};

 

void doSomething (Bird* pBird)

{

    cout << pBird->getName() << endl;

}

 

우리가 doSomething() 함수를 호출할 때는 다음 3가지 정도의 경우를 생각해볼 수 있습니다.

 

1) Bird 객체로 호출하는 경우

Bird bird;

doSomething(&bird);

 

2) Chicken 객체로 호출하는 경우(다형성)

Chicken chicken;

doSomething(&chicken);

 

3) Eagle 객체로 호출하는 경우(다형성)

Eagle eagle;

doSomething(&eagle);

 

그렇다면 우리가 원하는 doSomething() 호출 결과는 어떤 것일까요? 당연히 1)의 경우 "Bird"이고 2)는 "Chicken" 3)은 "Eagle" 이겠죠. 하지만 앞서 말했듯이 C++은 기본적으로 정적 바인딩을 사용합니다. 즉, 컴파일할 때 doSomething() 함수의 pBird는 Bird 객체이고, 따라서 무조건 Bird 클래스의 getName() 메서드를 호출하도록 만들어버립니다. 따라서, 실제로는 1)이던 2)던 3)이던 무조건 "Bird"가 출력됩니다. 이는 우리가 원하는 결과가 아닙니다. 따라서, "프로그램 실행 중 pBird가 실제로 가지고 있는 객체의 타입에 맞는 메서드 - 멤버 함수 - 를 호출" 해야할 필요가 있습니다. 이를 동적(실행 시간에 결정한다고 하여) 바인딩이라고 부르고, 이를 사용하기 위해서 virtual 키워드를 씁니다. 즉, 아래처럼 합니다.

 

class Bird {

    public:

        // 기반 클래스에만 virtual 키워드를 써주면 됩니다.

        virtual string getName { return string("Bird"); }

};

 

class Chicken : public Bird {

    public:

        string getName { return string("Chicken"); }

};

 

class Eagle : public Bird {

   public:

        string getName { return string("Eagle"); }

};

 

void doSomething (Bird* pBird)

{

    cout << pBird->getName() << endl;

}

 

이렇게 하면, 컴파일러는 실행 시간에 pBird가 어떤 객체의 포인터를 가지고 있는지를 판단하여 적절한 함수를 호출해주게 됩니다.

 

얼핏 보면 동적 바인딩이 더 좋아보이는 것 같지만, 실제로 동적 바인딩을 위해 컴파일러는 여러가지 정보를 추가적으로 유지해야 해서, 메모리 사용량과 수행 속도 면에서 약간의 손해를 보게 됩니다. JAVA가 대체적으로(대체적으로 입니다. 네이티브 컴파일링하면 이렇고 저렇고 태클 사양합니다) C++보다 느린 이유 중 하나가 기본적으로 동적 바인딩을 사용하기 때문입니다.

 

3. 다이아몬드형 상속에서 가상 기반 클래스 선언을 위한 virtual

다이아몬드 상속이란, 같은 기반 클래스를 가진 두 클래스를 동시에 상속받는 것을 이야기합니다. C++은 다중 상속이 가능하기 때문에 이런 문제가 발생하죠. 이러한 다중 상속은 득보다 실이 많다 하여 최근의 OOP 언어인 JAVA나 C#등에서는 지원하지 않습니다. 대신 interface 같은 것을 사용하죠.

 

어쨌든, 다이아몬드 상속의 예를 들어보겠습니다.

 

class A {

    // ...

};

 

class B : public A {

    // ...

};

 

class C : public A {

    // ...

};

 

class D : public B, public C {

    // ...

};

 

이런 형태의 상속은 기반 클래스 A가 B와 C에 각각 존재하므로 공간 낭비, 함수 호출 대상 모호 등의 문제점을 지니게 됩니다. 이를 해결하기 위해 가상 기반 클래스(virtual base class)를 사용하는데, virtual 키워드는 바로 이 가상 기반 클래스를 나타내기 위해 쓰입니다. 위 코드에서 가상 기반 클래스를 사용하면,

 

class A {

    // ...

};

 

class B : virtual public A {

    // ...

};

 

class C : virtual public A {

    // ...

};

 

class D : public B, public C {
    // ...

};

이렇게 됩니다. 

이 설명이 부족하다면, MSDN http://msdn.microsoft.com/library/default.asp?url=/library/en-us/vclang/html/_pluslang_Virtual_Base_Classes.asp 을 참고하세요.

참고:
http://kin.naver.com/detail/detail.php?d1id=1&dir_id=10104&docid=1355919&qb=YyMgdmlydHVhbA==&enc=utf8&pid=fWlG3loi5UhsssUA5c8sss--225492&sid=Stg43iY22EoAADYhI9k

여기서 긁었음

예전것/프로그래밍2009. 10. 2. 02:28

int WINAPI WinMain(...) 이것이 원래는  [ == int APIENTRY WinMain(...) ]

int FAR PASCAL WinMain(...) 이거였다고 하네요

WinMain() 함수는 프로그램 내에서 누가 호출하는 것이 아니고
윈도우 시스템에서 호출하는 함수인데

윈도우 시스템은
1. 64KByte를 넘는 포인터를 쓸 수 있는 시스템이고 : FAR
2. 함수 호출 방식이 PASCAL 방식입니다. : PASCAL

 

초기에는 FAR PASCAL WinMain(...) 이랬다가
FAR PASCAL 을 WINAPI로 바꿔쓰자 해서
요즘에는 WINAPI WinMain(....) 이라고 하네요. . --;; 복잡혀. .

결론 :
WINAPI == FAR PASCAL == APIENTRY

(Windef.h <-- 여기에 정의 되어 있습니다.)

 

' PASCAL 이란' __cdecl의 반대란 뜻인데
PASCAL이나 __cdecl은 에.... 일단은 함수 호출할 때'
함수의 인자가 호출되는 순서를 정하는 거라고 보시면 됩니다.

즉' __cdecl은 함수의 인자가 오른쪽에서 왼쪽으로 호출되고
PASCAL(__pascal)은 거꾸로 왼쪽에서 오른쪽으로 호출되고

원래 C 언어는 함수 호출할 때' 예를들어'
funcA(10' 20' 30) 이런식이다 하면....
funcA를 호출하면서' 30을 먼저 메모리에 넣어 주고'
다음으로 20' 마지막으로 10을 메모리에 넣어 주죠...

이 C언어와 같은 방식을 __cdecl이라 하고....

반대로 funcA(10' 20' 30)을 호출할때'
메모리에 10' 20' 마지막으로 30을 넣어주는 방식을
파스칼 방식이다... 해서 PASCAL(__pascal)이라고 하죠.

 

PASCAL 방식은 첫번째 목적은 다른 언어로 짜여진 obj 파일과 링크를 하기

위해서 존재하는 표현식이라고 할 수 있습니다.

그렇지만 VC++ 에서 많은 API 들이 이 PASCAL 방식의 호출을 사용한다고

알고 있습니다.

사실인지는 모르겠지만. . 이 방식이 표준 C 방식보다 호출이 빠르다고 하네요.

예전것/프로그래밍2009. 10. 1. 01:21

http://www.winapi.co.kr/project/library/gdiplus/1-1.htm

http://www.winapi.co.kr/

direct mobile 이란것도 생각해보자

http://xenostudy.tistory.com/27

===============전체 화면하기 ==========
http://www.devpia.com/MAEUL/Contents/Detail.aspx?BoardID=7083&MAEULNo=911&no=42261&ref=42250
이곳 참조 ..
데브피아가 답이다.

        case WM_ACTIVATE:
            SHHandleWMActivate(hWnd, wParam, lParam, &s_sai, FALSE);
   MoveWindow(hWnd, 0, 0, 240, 320, TRUE);
   SHFullScreen(hWnd, SHFS_HIDETASKBAR);
            break;
        case WM_SETTINGCHANGE:
            SHHandleWMSettingChange(hWnd, wParam, lParam, &s_sai);
   MoveWindow(hWnd, 0, 0, 240, 320, TRUE);
   SHFullScreen(hWnd, SHFS_HIDETASKBAR);
            break;

예전것/프로그래밍2009. 9. 12. 21:03

http://windowsmobile7.tistory.com/2
여기에 자세히 나와있다.
예전것/프로그래밍2009. 9. 12. 20:36

C:\Program Files\Microsoft Device Emulator\1.0
요기 경로에있다... 후 힘들었다.
예전것/프로그래밍2009. 9. 12. 00:05

#pragma once 

C의 헤더 파일 최상단에서 자주 볼 수 있는 이 코드는 컴파일러에게 이 헤더 파일이 한번만 빌드되도록 알려주는 명령입니다. 

왜 넣어야 하냐면, A.h라는 파일이 여러 곳에서 복잡하게 #include 되어 쓰이게 된다면 그때마다 각각 정의가 추가되게 되어 중첩되는 경우가 발생됩니다. 이 경우 중복 정의되었다는 에러가 발생하게 되지요. 즉 같은 내용이 여러번 빌드되게 되는겁니다. 이를 막기위해 #pragma once가 필요합니다. 물론 컴파일시간도 줄여주므로 대부분의 헤더파일에 추가하기를 추천합니다.

 

예전것/프로그래밍2009. 9. 11. 22:11

: error C2664: 'MessageBoxW' : 매개 변수 2을(를) 'const char [35]'에서 'LPCWSTR'(으)로 변환할 수 없습니다.

이것은 visual studio2008 문자집합 기본셋팅이 유니코드로 되 있기 때문이라고 합니다.
해결책으로 MessageBox함수 대신에 MessageBoxA함수를 써주면 된다고 하나 이것은 그 함수들만의 임시방편.

솔루션 탐색기에서 프로젝트이름부분에 마우스 오른쪽클릭하셔서 속성누르신다음에
(최상단에 있는 솔루션 '????????' (1 프로젝트) <- 이거선택하시면 안됩니다! 중요! 그 바로아래꺼..)
구성속성을 클릭하시면 오른쪽 항목들중에 '프로젝트 기본값' 밑쪽에 보시면 '문자 집합' 이 있습니다.
보시면 '유니코드 문자 집합 사용' 이 되어있을텐데요. '멀티바이트 문자 집합 사용'으로 바꿔줍니다

예전것/프로그래밍2009. 6. 25. 21:54
10 10
20 20
30 30
위의 코드에서 *array[3] 은 분명 3개의 주소를 저장하는 공간이다.
arr1, arr2, arr3의 주소를 저장하였다.

그런데 array[0][0] array[0][1] 이건뭔가?

뒤로 돌아가서

void main()
{
    char carr[3] = { 'A', 'B', 'C'};
    char *cp = carr;

   printf("%c %c %c", cp[0], cp[1], cp[2]);
}
위의 코드에서 cp[0]..이런식으로 접근이 가능하다는것을 배웠었다.
따라서 array[0] <= 이것자체는 arr1의 주소를 담고있다. array[0][0] 하면 0번째 인덱스의 값을 참조

그값은 바로 10이다.