1. C++ 가상 함수(Virtual Function)
1) 가상 함수(Virtual Function)란,
- 가상 함수는 기본 클래스(상속되지 않은 클래스) 내에서 선언되어 파생 클래스에 의해 재정의되는 맴버 함수
- 포인터(Pointer) 또는 기본 클래스에 대한 참조(Reference)를 사용하여 파생 클래스의 객체를 참조하면 해당 객체에 대해 가상 함수를 호출하고 파생 클래스의 함수를 실행할 수 있다.
- 주로 실행시간(Runtime)에 함수의 다형성(Polymorphism)을 구현하는데 사용된다.
- 기본 클래스 내에 virtual 키워드로 함수를 선언함.
2) 가상 함수 선언 규칙
- 클래스의 public 섹션에 선언함.
- 가상 함수는 정적(static)일 수 없으며 다른 클래스의 friend 함수가 될 수도 없다.
- 가상 함수는 실행시간 다형성을 얻기 위해 기본 클래스의 포인터 또는 참조를 통해 접근해야 한다.
- 가상 함수의 프로토타입(반환형과 매개변수)은 기본 클래스와 파생 클래스에서 동일하다.
- 클래스는 가상 소멸자를 가질 수 있지만, 가상 생성자는 가질 수 없다.
3) Ex.1
#include <iostream>
using namespace std;
class parent {
public :
virtual void v_print() {
cout << "parent" << "\\n";
}
void print() {
cout << "parent" << "\\n";
}
};
class child : public parent {
public :
void v_print() {
cout << "child" << "\\n";
}
void print() {
cout << "child" << "\\n";
}
};
int main() {
parent* p; // parent 클래스를 가리키는 포인터 p 선언
child c; // child 클래스의 객체 c 선언
p = &c; // 포인터 p는 c 객체를 가리킴.
// 몸체는 parent 클래스지만 실제 객체는 child 클래스이다.
p->v_print(); // 포인터 p를 이용해 가상 함수 v_print() 호출
p->print(); // 포인터 p를 이용해 오버라이딩 된 함수
// print() 호출
return 0;
}
/* 실행 결과
child
parent
*/
/*
v_print() 함수는 가상 키워드로 선언되어 가상 함수가 되었으며
가상 함수는 실행시간(런타임)에 그 값이 결정됩니다.
(후기 바인딩이라고도 합니다.)
포인터 p에는 child 클래스의 객체가 들어가 있고
**포인터가 가리키는 위치에 따라 child 클래스의
v_print() 함수가 호출**되었으며
일반 함수인 print()는 컴파일 시간에 이미 결정되기 때문에
31번째 줄에 p에서 print() 함수를 호출할 때
parent 클래스의 print() 함수가 호출되는것으로
결정이 끝나버린 상태입니다.
따라서 parent 클래스의 print() 함수가 호출되게 됩니다.
4) Ex.2
#include <iostream>
using namespace std;
class parent {
public :
void print1() { // 일반 함수 print1()
cout << "parent print1" << "\\n";
}
virtual void print2() { // 가상 함수 print2()
cout << "parent print2" << "\\n";
}
virtual void print3() { // 가상 함수 print3()
cout << "parent print3" << "\\n";
}
};
class child : public parent {
public :
void print2() { // 일반 함수 print2()
cout << "child print2" << "\\n";
}
void print3(int x) { // 오바라이딩 된 print3(int x)
cout << "child print3" << "\\n";
}
};
int main() {
parent* p;
child c;
p = &c;
p->print1();
p->print2();
p->print3();
return 0;
}
/* 실행 결과
parent print1
child print2
parent print3
*/
/*
print1() 함수는 일반 함수이기 때문에 컴파일 시간에 결정이 나고
parent 클래스의 print1() 함수가 호출됩니다.
print2() 함수는 가상 함수이기 때문에 실행 시간에 결정이 됩니다.
parent 클래스지만 가리키고있는 객체가 child 클래스이기 때문에
child 클래스의 print2() 함수가 호출되게 됩니다.
print3() 함수는 가상 함수이지만 child 클래스에서는
매개변수가 없는 함수를 호출했기 때문에
부모 클래스인 parent 클래스의 print3() 함수가 호출되게 된 것입니다.
*/