카테고리 없음

C++ - 가상(virtual) 함수,

동화다아아 2011. 11. 22. 16:30
- 가상(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가지가 없으면 문제가 생길 수 있다. 따라서 반드시 작성해 준다.