[C++] 다형성,virtual 함수

2021. 3. 3. 18:39Study/C&C++

다형성이란?

네이버 지식 백과에서는

다형성(polymorphism)이라는 단어는 원래 '여러 개의 형태를 갖는다'라는 의미의 그리스어에서 유래했다. 또 사전에서 찾아보면 poly(하나 이상), morph(형태)가 합성된 단어로 '하나 이상의 형태'를 뜻한다.

[네이버 지식백과] 다형성 (쉽게 배우는 소프트웨어 공학, 2015. 11. 30., 김치수)

 

이렇게 나와있다. 

음.. 하지만 이런게 프로그램을 만들때 왜 필요할까?? 

간단히 동물들의 울음 소리를 출력하는 프로그램을 만든다 가정해보자,

우선 각 동물들의 class를 만들고 특성에 맞는 울음소리를 출력해주는 함수 speak() 를 가진다.

 

class Cat 
{
private:
	string name;
public:
	Cat(const string& name_in)
		:Animal(name_in)
	{}
	void speak() const
	{
		cout << name<<" Mew " << endl;
	}
};

class Dog 
{
private:
	string name;
public:
	Dog(const string& name_in)
		:Animal(name_in)
	{}
	void speak() const
	{
		cout <<name <<" Wal " << endl;
	}
};

위와 같이 class 가 선언 되어 있고 울음소리를 출력 해보자.

	Cat c("junic");
	Dog d("sion");
	c.speak();// junic mew 출력
	d.speak();// sion wal 출력

	Cat c_arr[] = { Cat("cat1"),Cat("cat2") ,Cat("cat3") ,Cat("cat4") };
	Dog d_arr[] = { Dog("dog1"),Dog("dog2") ,Dog("dog3")};

	for (auto& element : c_arr)
		element.speak(); //cat1~4 mew 출력

	for (auto& element : d_arr)
		element.speak(); //dog1~3 wal 출력

하지만, 이렇게 다른 자료형 마다 출력 문을 따로 하기 힘들지 않을까? 또 여러 동물들을 가지게 된다면 

너무 번거로울 것이다.

 

두 클래스의 공통점을 보면

모두 같은 기능의spaek() 함수를 가지고 있고 각 동물 특성에 맞게 다른 울음 소리를 출력 해준다. 

이러한 다형성을 이용하여, 

class Animal
{
protected:
	string name;
public:
	Animal(const string& name_in)
		:name(name_in)
	{}
	void speak() const
	{
		cout << name<<" ?? " << endl;
	}
};
class Cat : public Animal
{
private:

public:
	Cat(const string& name_in)
		:Animal(name_in)
	{}
	void speak() const
	{
		cout << name<<" Mew " << endl;
	}

};
class Dog : public Animal
{
private:

public:
	Dog(const string& name_in)
		:Animal(name_in)
	{}
	void speak() const
	{
		cout <<name <<" Wal " << endl;
	}
};

 

Animal은 소리를 낸다라는 공통 점으로 묶고 동물 특성에 맞게 함수를 오버라이딩(overriding) 해준다.

Animal* a_ptr[] = {&c_arr[0],&c_arr[1] ,&c_arr[2] ,&c_arr[3],
						&d_arr[0],&d_arr[1],&d_arr[2]};
	for (auto& element : a_ptr)
		element->speak();

그후 편하게 부모 class 인 Animal형 pointer로 묶어서 7마리의 동물의 울음 소리를 모두 출력 해주려 했다.

하지만 

 

위와 같은 이유로 모두 Ainmal class 에 정의된 speak() 함수를 따라 ??? 을 출력 할 것이다.

가상함수, Virtual 

다형성을 이용하여 class 를 잘 정의 했는데 뭔가 부족한 느낌이 든다.

오버라이딩(overriding)된 함수들도 공통된 부모 class 포인터 하나로 이용할 수 없을까? 

이때 사용 하는게 Virtual 키워드 이다.

class Animal
{
protected:
	string name;
public:
	Animal(const string& name_in)
		:name(name_in)
	{}
	virtual void speak() const
	{
		cout << name<<" ?? " << endl;
	}
};

위처럼 Animal Class 에 speak() 함수 앞 virtual 키워드를 붙여 준다. 

** 부모 Class 에 있는 함수 앞에 virtual 키워드를 붙여주면 자식 클래스 에게는 자동 적용 이다.

Animal* a_ptr[] = {&c_arr[0],&c_arr[1] ,&c_arr[2] ,&c_arr[3],
						&d_arr[0],&d_arr[1],&d_arr[2]};
	for (auto& element : a_ptr)
		element->speak();

그 후 다시 이 코드를 실행하면, 각 객체의 특성에 맞게 함수가 호출된다.

그런데.. 어떻게 가능한 것인가? Animal* 은 Animal 객체에 맞는 부분만 접근 할 수 있을텐데? 

Virtual Table

Virtual Table 이란 일종의 안내서 같은 느낌인데 Class 에서  Virtual 함수들이 실행해야할 함수의 위치로 안내해 준다.

 

class 에서 virtual 로 함수를 선언하게되면 각 class 별로 Virtual Table을 가지게 되고, 

 Virtual Table 을 가리키는 function Pointer를 멤버로 가지고 있다.(물론 보이진 않는다)

그래서 함수를 호출 할 시 virtual 함수들은 Virtual Table에서 실행할 함수를 찾아서 실행하게 되고,

일반 함수들은 직접 호출 된다. (당연히 가상 함수들은 좀 느릴 수 있다.)

이와 같은 원리로 

Animal 포인터가 는 실제로 Cat 객체를 가리키고 있으므로

Cat 객체의 Virtual Table 보고 함수를 실행한다.

 

 

공통된 함수를 Virtual 로 선언 해주면 객채의 Virtual Table 을 보고 알맞은 함수를 실행한다.

 

실제로 디버깅 해보면 vfptr을 가지고 있다.

 

요약

1. 다형성 - 같지만 다른특성 -> 상속후 오버라이딩

2. 오버라이딩한 함수 Virtual 으로 선언 -> 묶어서 실행 가능.

 

 

'Study > C&C++' 카테고리의 다른 글

[C++] std::cin, getline()  (0) 2021.02.22
[C++]R-value Reference,move Sementics  (0) 2021.02.20
[C++] l-value,r-value  (0) 2021.02.14
[C++] C -style string , std::string  (0) 2021.02.11
[C++] Stack and Heap(feat. Memory Leak)  (0) 2021.02.11