본문 바로가기
코스웨어/11년 내장형하드웨어

[내장형]김동화 - 11월22일 일일보고서

by 알 수 없는 사용자 2011. 11. 22.
728x90
반응형
C++ [가상(virtual)함수, 순수 가상 함수]
- 가상(virtual) 함수
   포인터  
 가상이 아닌 함수  정적 기반 호출  
 가상 함수  동적 기반 호출  
 → 예제
// 가상함수
#include<iostream.h>


class Musician
{
  private:

  public:
    // virtual - 각각의 자식 클래스들을 사용하게 해 준다.
    // 오버 라이딩
    virtual void greet() // MFC에서 매우 중요한 기법
    {
      cout << "안녕하세요 저는 뮤지션입니다." << endl;
    }    
};
class Singer:public Musician
{
  private:
    
  public:
    void greet()
    {
      cout << "안녕하세요 저는 싱어입니다." << endl;
    }    
};
class Drum:public Musician
{
  private:
    
  public:
    void greet()
    {
      cout << "안녕하세요 저는 드러머입니다." << endl;
    }
};
class Bell:public Musician
{
  private:
    
  public:
    void greet()
    {
      cout << "안녕하세요 저는 벨 연주자입니다." << endl;
    }
};
int main()
{
  Musician M;
  Singer S;
  Drum D;
  Bell B;
  Musician *P[4= {&M, &S, &D, &B}; // 각각을 초기화
  
  for(int iCnt = 0 ; 4>iCnt ; ++iCnt)
  {
    (*P[iCnt]).greet();
  }  
  
/*
  M.greet();
  S.greet();
  D.greet();
  B.greet();
  */

  return 0;
}

 → 예제 Musician의 상속자 Drum, Singer, Bell을 생각해 보면
 → 4개의 클래스 모두 공통된 void greet() 함수를 가지고 있다.(오버 라이딩)
 → 따라서 이를 호출하기 위한 동적 기반 호출 방법을 살펴보면
 → M.greet(); D.greet();.... or 각 타입의 포인터 P로 P->greet() 으로 호출한다.
 → 하지만 이런 방법들은 필요한 호출 횟수마다 일일이 직접 호출해 주어야 하는 단점이 있다.

 → 따라서 virtual함수를 이용한 동적기반 호출을 확인해 보면
 → 최상위 클래스(object class)인 Musician의 void greet()함수 앞에 virtual을 붙인다.( → virtual void greet())
 → 반복문을 이용하여 P[0]->greet();... 과 같은 형식으로 출력한다.(virtual로 함수를 만들고 오버라이딩으로 호출)
 → virtual 함수를 이용한 동적기반 호출로 전혀 다른 타입을 묶어서 관리 할 수 있다.
 → 실행 결과

 → 가상 함수는 상속 받아서 새로 만들지 않아도 된다.
 → 순수가상은 반드시 재정의 해야 하며, 가상함수는 재정의 하지 않는다.
 → 즉, 순수 가상함수로 해 놓으면 반드시 상속 클래스에서 해당 함수를 재정의 하도록 강제 할 수 있다.
※ 자바는 다중 상속을 지원하지 않으므로 인터페이스 클래스를 지원한다.(클래스라는 말을 빼고 인터페이스라고 한다.)
※ disassembly  → 실행파일을 역 어셈블리 할 수 있다.(기계어를 어셈블리로 바꾸어주는 것)
 → 알고리즘을 공개하지 않는 소스의 실행파일을 disassambly로 일부 획득할 수 있다.
 → 하지만 가상함수로 만들면 disassembly로 분석이 거의 힘들며 따라서 보안상의 이점이 있다.

 → Music *P = new Rock;, void print(); 적용 예제
#include<iostream.h>

class
 Music
{
  private:

  public:
    int *P;
    Music()
    {
      P = new int;
      *P = 100;
      cout << "Music 생성자 출력" << endl;
    }
    virtual ~Music() // 상속 받은 Rock의 소멸자가 구동되게 한다.
    {
      delete P;
      cout << "Music 소멸자 출력" << endl;
    }
    void print(ostream *O)
    {
      *O << "Music 클래스 P 값 : " << *P << endl;
    }
};
class Rock:public Music
{
  private:

  public:
    int *P2;
    Rock()
    {
      P2 = new int;
      *P2 = 200;
      cout << "Rock 생성자 출력" << endl;
    }
    ~Rock()
    {
      delete P2;
      cout << "Rock 소멸자 출력" << endl;
    }
    void print(ostream *O)
    {
      Music::print(O);
      *O << "Rock 클래스 P2 값 : " << *P2 << endl;
    }
};
ostream &operator << (ostream &O, Music &r)
{
  r.print(&O);
  return O;
}
ostream &operator << (ostream &O, Rock &r)
{
  r.print(&O);
  return O;
}
int main()
{
  Rock OBJ1;  
  operator<<(cout, OBJ1);  
  // Music *P = new Rock; // Rock은 Music을 상속 받으므로 가능
  // 원래는 Music소멸자만 호출되고 Rock 소멸자는 호출되지 않아
  // 메모리 상에 4byte가 남아있게 되지만
  // Music 소멸자에 virtual을 붙임으로 Rock의 소멸자도 동작하게 한다.
  // delete P;
  
  return 0;
}

 → 실행 결과

 → Music *P = new Rock; 의 결과는 주석으로 첨부한다.
 → class Music의 P와 class Rock의 P2의 값을 print()로 출력하였다.

- 소멸자, 출력함수는 가상 함수로 만들어야 한다.
- 생성자, 소멸자, 대입연산자, 복사 생성자
 → 동적 할당시 이 4가지가 없으면 문제가 생길 수 있다. 따라서 반드시 작성해 준다.


MFC
- MFC 기본 예제 출력
 →  HelloMFC.cpp 예제
#include<afxwin.h// AFX(Application Frameworks)

// 응용 프로그램 클래스를 선언한다.
class CHelloApp : public CWinApp
{
public:
  virtual BOOL InitInstance();
};

// 메인 윈도우 클래스를 선언한다.
class CMainFrame : public CFrameWnd
{
public:
  CMainFrame(); // 생성자
protected:
  // afx_msg는 메시지 큐에 들어가는 함수라는 표시
  afx_msg void OnPaint();
  afx_msg void OnLButtonDown(UINT nFlags, CPoint point);
  DECLARE_MESSAGE_MAP()
};
// 응용 프로그램 객체를 선언한다.
CHelloApp theApp;
// 응용 프로그램 클래스를 정의한다.
BOOL CHelloApp::InitInstance()
{
  m_pMainWnd = new CMainFrame;
  m_pMainWnd -> ShowWindow(m_nCmdShow);
  return TRUE;
}
// 메인 윈도우 클래스를 정의한다.
CMainFrame::CMainFrame() // 생성자 정의
{
  Create(NULL, "HelloMFC Application"); // WIN32 API의 CreateWinodw()호출과 같다.
                                        // MFC는 위와 같이 win32의 내용이 클래스화 되어 있다.

}
void CMainFrame::OnPaint()
{
  char *msg = "Hello, MFC";
  CPaintDC dc(this);
  dc.TextOut(100100, msg, lstrlen(msg));
}
void CMainFrame::OnLButtonDown(UINT nFlags, CPoint point)
{
  MessageBox("마우스를 클릭했습니다.""마우스 메시지");
}
// 메시지맵을 선언한다.
BEGIN_MESSAGE_MAP(CMainFrame, CFrameWnd)
  ON_WM_PAINT()
  ON_WM_LBUTTONDOWN()
END_MESSAGE_MAP()

 → 실행 결과


 - 클래스 함수 포인터 구현 예제
// 클래스 함수 포인터
#include<iostream.h>

class A
{
  public:
    void TEST()
    {
      cout << "Hello" << endl;
    }
};
int main()
{
  A OBJ;
  OBJ.TEST();
  void (A::*fp)() = OBJ.TEST;
  (OBJ.*fp)();
  
  return 0;
}


 → 위와 같이 C++에서 함수 포인터를 사용할 수 있다.



 →
#include <windows.h>

#define DECLARE_MESSAGE_MAP()          static MessageMap messageMap[];
#define BEGIN_MESSAGE_MAP(class_name)  MessageMap class_name::messageMap[]={
#define END_MESSAGE_MAP()              {0,NULL}};

//Forward declaration------------------------------------------------
LRESULT CALLBACK WndProc(HWND hwnd,UINT iMsg,WPARAM wParam,
                         LPARAM lParam);

//Class CObject------------------------------------------------------
class CObject {
protected:
    static char szAppName[];
    HWND        hwnd;
    MSG         msg;
    WNDCLASSEX  wndclass;

public:
    void InitInstance(HINSTANCE hInstance,PSTR szCmdLine,
                      int iCmdShow);
    void Run();
    WPARAM ExitInstance();
    // message handler isn't needed. Wow!
};//class CObject

void CObject::InitInstance(HINSTANCE hInstance,PSTR szCmdLine,
                        int iCmdShow) {
    wndclass.cbSize         = sizeof(wndclass);
    wndclass.style          = CS_HREDRAW|CS_VREDRAW;
    wndclass.lpfnWndProc    = WndProc;
    wndclass.cbClsExtra     = 0;
    wndclass.cbWndExtra     = 0;
    wndclass.hInstance      = hInstance;
    wndclass.hIcon          = LoadIcon(NULL,IDI_APPLICATION);
    wndclass.hCursor        = LoadCursor(NULL,IDC_ARROW);
    wndclass.hbrBackground  = (HBRUSH)GetStockObject(WHITE_BRUSH);
    wndclass.lpszMenuName   = NULL;
    wndclass.lpszClassName  = szAppName;
    wndclass.hIconSm        = LoadIcon(NULL,IDI_APPLICATION);

    RegisterClassEx(&wndclass);

    hwnd = CreateWindow(szAppName,    //window class name
        "The Hello Program",        //window caption
        WS_OVERLAPPEDWINDOW,        //window style
        CW_USEDEFAULT,              //initial x position
        CW_USEDEFAULT,              //initial y position
        CW_USEDEFAULT,              //initial x size
        CW_USEDEFAULT,              //initial y size
        NULL,                       //parent window handle
        NULL,                       //window menu handle
        hInstance,                  //program instance handle
        NULL);                      //creation parameters

    ShowWindow(hwnd,iCmdShow);
    UpdateWindow(hwnd);
}//CObject::InitInstance

void CObject::Run() {
    while (GetMessage(&msg,NULL,0,0)) {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }//while
}//CObject::Run

WPARAM CObject::ExitInstance() {
    return msg.wParam;
}//CObject::ExitInstance

char CObject::szAppName[]="HelloWin"// 창제목(초기화)

// MessageMap, It's punch line! ----------------------------------
class CView; // 함수 원형을 미리 선언한 것과 같다.
typedef void (CView::*CViewFunPointer)(); // 클래스 펑션 포인터(class function pointer)
                    // 클래스안에 있는 함수 포인터를 가리킨다.
typedef struct tagMessageMap {
    UINT iMsg;
    CViewFunPointer fp;
} MessageMap;
static CViewFunPointer fpCViewGlobal;//pointer to a member function

// class CView----------------------------------------------------
class CView : public CObject {
public:
  // MFC에서 자동으로 메시지를 추가/삭제할 때마다 함수 이름이 자동으로 추가/삭제한다.
  // AFX가 들어간 자리는 MFC가 건드린다.
    // 1) you must properly declare message handler 
    //{{AFX_MESSAGEMAP
    void OnCreate();
    void OnDraw();
    void OnDestroy();
    //}}AFX_MESSAGEMAP

    // 2) you must include this macro in your class header
    DECLARE_MESSAGE_MAP()
  // static MessageMap messageMap[];
};//class CView

// 3) next, you add your event handler message map, it's array of function pointer
BEGIN_MESSAGE_MAP(CView)
    {WM_CREATE,CView::OnCreate},
    {WM_PAINT,CView::OnDraw},
    {WM_DESTROY,CView::OnDestroy},
END_MESSAGE_MAP()

//CView Event handler------------------------------------------------
void CView::OnCreate() {
}//CView::OnCreate

void CView::OnDraw() {
    HDC         hdc;
    PAINTSTRUCT ps;
    RECT        rect;

    hdc = BeginPaint(hwnd,&ps);

    GetClientRect(hwnd,&rect);
    DrawText(hdc,"Hello, Windows!",-1,&rect,
        DT_SINGLELINE|DT_CENTER|DT_VCENTER);
    
    EndPaint(hwnd,&ps);
}//CView::OnDraw

void CView::OnDestroy() {
    PostQuitMessage(0);
}//CView::OnDestroy

//Global object------------------------------------------------------
CView app;

//Window procedure---------------------------------------------------
LRESULT CALLBACK WndProc(HWND hwnd,UINT iMsg,WPARAM wParam,
                         LPARAM lParam) {
    int i=0;

    while ( 0 != CView::messageMap[i].iMsg ) {
        if ( iMsg == CView::messageMap[i].iMsg ) {
            fpCViewGlobal = CView::messageMap[i].fp;
            (app.*fpCViewGlobal)();
            return 0;
        }//if
        ++i;
    }//while
    return DefWindowProc(hwnd,iMsg,wParam,lParam);
}//WndProc

int WINAPI WinMain(HINSTANCE hInstance,HINSTANCE hPrevInstance,
                   PSTR szCmdLine,int iCmdShow) {
    app.InitInstance(hInstance,szCmdLine,iCmdShow); // 창 생성
    app.Run(); // 메시지를 받는다.
    return app.ExitInstance();
}//WinMain

 → 위의 소스를 주석과 함께 살펴 보면 각각이 WINAPI의 내용을 나누어 둔 것임을 확인할 수 있다.

 → 아래의 모양을 따라 소스가 구현되어 있음을 확인해 본다.
BEGIN_MESSAGE_MAP CView::messageMap[]={
    {WM_CREATE    ,CView::OnCreate},
    {WM_PAINT       ,CView::OnDraw},
    {WM_DESTROY  ,CView::OnDestroy},
    {0                      ,NULL}
}; //전역변수의 초기화
// 클래스를 아무리 많이 만들어도 하나만 생성된다.(static이므로)
// 몇개의 객체를 생성하든 똑같이 사용하므로(참조만 할것이므로) static을 주어 한개만 만들어 지게 한다.

 → 시간 관계상 소스에 작성된 주석들을 따로 정리하지 못했습니다. 
 → 부족한 부분이 많습니다. 많은 보충 부탁드립니다. ㅋㅋ
728x90