0. 가상 함수란?

  • 파생 클래스에서 '재정의'(오버라이딩) 할 것으로 기대되는 함수에 붙이는 키워드, virtual

 

1. 가상화 조건

  • UpCasting 된 포인터 변수에서 virtual로 정의된 함수를 부르게 된다면 실제 인스턴스에 맞는 함수를 불러줌
  • 동적 바인딩이라고도 한다.
  • 런타임에 실제 불러야 할 함수를 가상함수 테이블에서 찾아 함수를 불러주는 것
#include <iostream>

class Parent
{
public:
	virtual void PrintMyName()
	{
		std::cout << "Parent" << std::endl;
	}
};

class Child : public Parent
{
public:
	virtual void PrintMyName()
	{
		std::cout << "Child" << std::endl;
	}
};

int main()
{
	Parent P;
	Child C;

	Parent* p = &P;	//본인의 자료형에 맞는 인스턴스를 갖는 경우
	p->PrintMyName();
	p = &C;	//이 상황을 UpCasting이라 할 수 있음, Child는 Parent의 자식이기 때문에 부모 포인터로도 자식을 가리킬 수 있음
	p->PrintMyName();

	return 0;
}

 

2. 가상함수 테이블

  • 클래스 내 virtual 키워드가 붙은 함수가 하나라도 있다면 그 클래스는 가상함수 테이블이라는 것을 가지게 된다.
  • virtual이라는 키워드가 붙은 만큼 숨겨 놓은 포인터를 만드는 거다. 그 포인터는 virtual 키워드가 붙은 함수의 주소를 가리키게 된다.
  • 오버라이딩 된 함수는 각각 다른 주소를 가질 것이고 그 주소를 숨긴 포인터가 가리키게 된다. 이 때 파생 클래스 입장에선 새로운 포인터를 만드는 것이 아니고 기존 부모가 물고 있던 포인터의 주소를 바꾸는 방식이다.
  • 실제 virtual로 정의된 함수를 부르게 되면 가상함수 테이블에서 그 함수의 주소를 가지고 있을 것이라 기대되는 포인터로부터 주소를 얻어와 부르게 된다.
  • virtual을 붙인 것만으로 그 class는 포인터 크기(4byte 혹은 8byte)만큼 크기가 커지고 그 함수를 호출할 때 가상함수를 뒤져봐야 하는 부하가 걸릴 것이다.

 

3. 추상 클래스

  • 순수 가상 함수를 하나라도 포함하고 있다면 추상 클래스이다.
  • 추상 클래스는 인스턴스화 될 수 없다.
  • 순수 가상 함수란 함수의 구현부 대신 = 0; 으로 대신한 함수를 의미한다.
  • 추상 클래스도 포인터나 래퍼런스로서 활용될 수 있다.
#include <iostream>

class Parent
{
public:
	virtual void PrintMyName() = 0;
};

class Child1 : public Parent
{
public:
	virtual void PrintMyName()
	{
		std::cout << "Child1" << std::endl;
	}
};

class Child2 : public Parent
{
public:
	virtual void PrintMyName()
	{
		std::cout << "Child2" << std::endl;
	}
};

int main()
{
	//Parent P;	//error

	Parent* P1 = new Child1;
	Child2 C2;
	Parent& P2 = C2;

	P1->PrintMyName();
	P2.PrintMyName();

	return 0;
}

 

4. 인터페이스

  • 순수 가상 함수로만 구성된 클래스 혹은 구조체
  • 인터페이스를 상속받는 클래스들은 공통의 함수를 구현하길 기대함
  • 접두어로 I를 붙이기로 약속이 되어 있음
  • C++의 정식 문법은 아니고 클래스와 순수 가상 함수의 특성을 적절히 조합하여 인터페이스를 흉내냄
  • 인터페이스의 정의상으론 멤버 변수가 있으면 안됨
  • 추상 클래스와 인터페이스의 경계선이 모호한 것이 현실.

 

5. 가상 소멸자

  • UpCasting된 포인터 변수가 소멸할 때 본인 인스턴스의 소멸자를 찾아 부르기 위함(상속관계에 있다면 인스턴스를 상위계층의 호출자도 모두 부름)
  • 위 예제에서도 virtual이 붙은 소멸자가 없기 때문에 Child 본인의 소멸자를 부르지 못하고 Parent의 소멸자를 불렀을 것

'C++ > 키워드 정리' 카테고리의 다른 글

키워드 정리 [15] - SOLID  (2) 2023.12.01
키워드 정리 [14] - RTTI  (1) 2023.12.01
키워드 정리 [12] - 클래스  (4) 2023.12.01
키워드 정리 [11] - OOP  (1) 2023.12.01
키워드 정리 [10] - Template  (0) 2023.12.01

+ Recent posts