클래스의 상속에서 오버라이딩에 대해 배웠고, 업캐스팅을 했을 때 오버라이딩된 함수가 어떻게 호출되는지에 대해서도 알아보았다.
함수 호출을 해당 함수의 정의와 결합해 둔 것
컴파일할 때 미리 호출될 함수를 결정하고, 실행 시에는 컴파일 시점에 바인딩된 함수를 실행하는 결합 방법
컴파일할 때 호출될 함수를 결정하지 않고, 프로그램이 실행 중일 때 결정되는 함수의 결합 방법
virtual 키워드를 붙인 함수를 가상 함수라고함
class Parent
{
public:
Parent() { cout << "Parent()" << endl; }
virtual void Print() { cout << "Parent Print()" << endl; }
};
class Child : public Parent
{
public:
Child() { cout << "Child()" << endl; }
virtual void Print() { cout << "Child Print()" << endl; }
void PurePrint() { cout << "PurePrint()" << endl; } // virtual 키워드를 사용하지 않아도 가상 함수임
};
int main()
{
Child* c = new Child;
Parent* pp = c;
Child* pc = (Child*)pp;
c->Print();
pp->Print();
pc->Print();
pp->PurePrint();
pc->PurePrint();
delete c;
}
// 출력 결과
// Parent()
// Child()
// Child Print()
// Child Print()
// Child Print()
동적 바인딩이 된 소멸자
class Parent
{
virtual ~Parent();
}
부모 클래스 포인터에 자식 객체가 들어있는 경우, 이 때 할당 해제를 하게 되면 부모 소멸자만 호출되게 된다. 그럼 자식 클래스의 소멸자는 호출되지 않게 되므로 자식 클래스 영역에서 할당 해제해야하는 변수들이 있으면 이를 관리하지 못하게 된다. 그런 이유로 소멸자를 가상 소멸자로 만들어, 부모 클래스 포인터에서 자식 객체를 할당 해제 시켜도 동적 바인딩에 의해 자식 클래스의 소멸자가 호출된다.
class Parent
{
public:
virtual ~Parent() { cout << "~Parent()" << endl; }
};
class Child : public Parent
{
public:
~Child() { cout << "~Child()" << endl; }
};
int main()
{
Child* c = new Child;
Parent* pp = c;
delete pp; // 부모 소멸자만 호출되는 것이 아니고, virtual 소멸자이기 때문에 자식 소멸자까지 호출됨
}
// 출력 결과
// ~Child()
// ~Parent()
완전 가상 함수를 가진 클래스
함수의 몸체 부분이 없이, 자식 클래스에서 오버라이딩하여 사용해야하는 함수
virtual 반환형 함수명() = 0;
class Parent
{
public:
Parent() { cout << "Parent()" << endl; }
virtual void PurePrint() = 0;
};
class Child : public Parent
{
public:
Child() { cout << "Child()" << endl; }
void PurePrint() { cout << "PurePrint()" << endl; } // virtual 키워드를 사용하지 않아도 가상 함수임
};
int main()
{
Child* c;
Parent* pp = &c;
Child* pc = (Child*)pp;
pp->PurePrint();
pc->PurePrint();
}
// 출력 결과
// Parent()
// Child()
// PurePrint()
// PurePrint()