C++

[C++] 가상 함수(Virtual function)와 소멸자 가상화

SWBlossom 2019. 12. 27. 12:29

가상 함수란?

가상 함수(Virtual function)는 virtual 예약어를 붙여서 선언한 메서드이다. 가상 함수는 과거의 정의가 완전히 무시된다는 것이 특징이다.

 

사용 방법은 다음과 같다.

virtual 반환형식 메서드이름

 

함수앞에 virtual 예약어만 붙여주면 된다.

 

#include <iostream>
using namespace std;

//부모 클래스
class PClass
{
public:
	//가상 함수
	virtual void printNum()
	{
		cout << "PClass num : " << num << endl;
	}

	void TestFunc()
	{
		cout << "TestFunc()" << endl;
		printNum();
	}

protected:
	int num = 10;
};

//파생 클래스
class CClass : public PClass
{
public:
	//가상 함수 재정의
	virtual void printNum()
	{
		cout << "CClass: " << num*2<< endl;
	}
};

int main()
{
	CClass a;	//파생 클래스 객체생성
	a.printNum();

	PClass& b = a;
	b.printNum();

	a.TestFunc();

	return 0;
}

 

결과값:

CClass: 20	//a.printNum()의 결과
CClass: 20	//b.printNum()의 결과
TestFunc()	//a.TestFunc()의 결과
CClass: 20

 

virtual 예약어를 통해 기존의 부모 클래스에서 정의된 메소드가 완전히 오버라이드된 것을 확인할 수 있다. 가상 함수는 재정의된 메소드가 있다면 그 함수를 호출하는 것이다.

 

또한, 상 함수는 일반 메소드와 달리 참조 형식과 관계없이 실 형식의 메소드를 호출한다. 만약, 부모 클래스에서 virtual 예약어를 지운다면 b.printNum(); 코드에 의해 'PClass : 10'이라는 결과가 나왔을 것이다. 일반 메소드는 참조 형식에 의해 어떤 메소드가 호출되는지 결정되기 때문이다.

 

마지막으로, TestFunc() 함수에서 printNum() 함수를 호출하는데 이 printNum()은 가장 마지막에 재정의된 함수를 호출한다.

 


부모 클래스에 대한 포인터나 참조자로 파생 클래스 인스턴스를 참조할 수 있다.

 

부모클래스 *a = new 파생클래스;

 

#include <iostream>
using namespace std;

//부모 클래스
class PClass
{
public:
	PClass() { name = new char[10]; }
	~PClass() 
	{ 
		cout << "~PClass()" << endl;
		delete name;
	}

private:
	char* name;
};

//파생 클래스
class CClass : public PClass
{
public:
	CClass() { age = new int; }
	~CClass()
	{
		cout << "~CClass()" << endl;
		delete age;
	}
private:
	int* age;
};

int main()
{
	PClass* a = new CClass;

	delete a;

	return 0;
}

 

결과값:

~PClass()

 

이 코드에는 문제점이 있다. 부모 클래스의 소멸자 호출은 이루어지나 파생 클래스의 소멸자는 호출되지 않는다. 즉, 참조 형식의 소멸자만 호출되고 실 형식의 소멸자는 호출되지 않는다.

 

소멸자를 가상화하여 해결할 수 있다.

virtual ~PClass()
{
	cout << "~PClass()" << endl;
	delete name;
}

 

결과값:

~CClass()
~PClass()

 

*상속 관계에서 가상 함수는 한 번 가상 함수면 영원히 가상 함수다. 즉 부모 클래스에서 가상 함수이면 파생 클래스에서 virtual 예약어를 사용하지 않더라도 자동으로 가상화된다.