C++ - 가상(virtual) 함수,
카테고리 없음 / 2011. 11. 22. 16:30
- 가상(virtual) 함수
→ 예제
→ 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(); 적용 예제
→ Music *P = new Rock; 의 결과는 주석으로 첨부한다.
→ class Music의 P와 class Rock의 P2의 값을 print()로 출력하였다.
포인터 | ||
가상이 아닌 함수 | 정적 기반 호출 | |
가상 함수 | 동적 기반 호출 |
// 가상함수
#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을 생각해 보면#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;
}
→ 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;
}
→ 실행 결과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;
}
→ class Music의 P와 class Rock의 P2의 값을 print()로 출력하였다.
- 소멸자, 출력함수는 가상 함수로 만들어야 한다.
- 생성자, 소멸자, 대입연산자, 복사 생성자
→ 동적 할당시 이 4가지가 없으면 문제가 생길 수 있다. 따라서 반드시 작성해 준다.
- 생성자, 소멸자, 대입연산자, 복사 생성자
→ 동적 할당시 이 4가지가 없으면 문제가 생길 수 있다. 따라서 반드시 작성해 준다.