본문 바로가기

강의자료/Class

006. 가상(virtual) 함수에 대해서 알아보자.

◈ 이 글은 강의를 위하여 제가 직접 작성한 내용입니다. 따라서 퍼가실 경우, 출처를 명확히 해주시기 바랍니다!!
◈ This ariticle was written by me for teaching. So if you want to copy this article, please clarify the source!!


클래스의 꽃이라고 불리우는 다형성을 사용하기 위한 가장 기초적인 기능인 가상함수에 대해서 알아본다.
// 선수지식 1 : 부모클래스의 포인터는 자식클래스의 인스턴스를 가리킬 수 있지만, 자식클래스의 포인터는 부모클래스의 인스턴스를 가리킬 수 없다.
// (부모클래스의 포인터로 사용가능한 함수는 모두 자식클래스가 가지고 있지만, 자식클래스의 포인터로 사용가능한 함수는 부모클래스에 존재하지 않을 수 있기때문이다.)

// 선수지식 2 : 부모클래스의 포인터가 자식클래스의 인스턴스를 가리키고 있고, 부모와 자식이 동일한 이름의 함수를 가지고 있을 경우, 부모클래스의 포인터를 이용하여 해당 함수를 호출하면, 실제 인스턴스가 자식클래스의 것이라 하더라도 부모클래스의 함수가 호출이 된다. 이를 방지하고 실제 인스턴스인 자식클래스의 함수가 호출되도록 하기 위한 기능이 바로 가상함수이다.

// 가상함수란 : 부모클래스의 포인터를 통해 자식클래스의 함수가 호출되게 하는 것. 이를 위해선 부모클래스의 함수를 가상함수로 선언하기만 하면 된다.
// 가상함수는 그 자체적으로는 효용성이 없으나 클래스의 다형성을 이용하기 위해서는 꼭 필요한 기능이다. 
// 그러므로 우선은 사용법만 익혀두자. 왜 필요하며 어디에 사용하는지는 나중에 다형성을 공부하다보면 자연히 익히게 된다.


// 주의 사항 : 가상함수를 사용할 경우에는 부모 클래스의 소멸자를 반드시 가상함수로 만들어야 메모리 누수를 막을 수 있다. 
// (소멸자를 가상함수로 만들지 않을 경우, 메모리 해제시 부모클래스의 소멸자만 실행되고 자식클래스의 소멸자는 실행이 되지 않는다.)

// 팁 : virtual 키워드는 부모클래스에만 적어주면 적용된다. 자식클래스에는 적어줘도 되고 적어주지 않아도 된다.
// (자식 클래스에 virtual 키워드를 적어주는 이유는 기능적인 이유가 아니라 단지 이 함수가 가상 함수라는 것을 알리기 위한 표현일 뿐이다.)

#include 
#include 
using namespace std; 

class Mammal
{
public:
	void Move() const { cout << "Mammal move one step\n"; }

public:
	virtual void Speak() const { cout << "Mammal speak!\n"; }

public:
	Mammal():itsAge(1) { cout << "Mammal constructor...\n"; }
	virtual ~Mammal() { cout << "Mammal destructor...\n"; }

protected:
	int itsAge;
};

class Dog : public Mammal
{
public:
	void Move() const { Mammal::Move(); cout << "Dog moves 5 steps...\n"; }

public:
	void WagTail() { cout << "Wagging Tail...\n"; }
	virtual void Speak() const { cout << "Woof!\n"; }

public:
	Dog() { cout << "Dog Constructor...\n"; }
	virtual ~Dog() { cout << "Dog Destructor...\n"; }
};

void main()
{
	Mammal* pDog = new Dog;			// 상속관계만 가능.
	//Dog* pMammal = new Mammal;	// 불가능.

	pDog->Move();		// Mammal::Move() 호출.
	pDog->Speak();		// Dog::Speak() 호출.
	//pDog->WagTail();	// 불가능.

	delete pDog;		// virtual로 되어있어야 부모/자식이 모두 소멸자가 호출이 된다.
}