2021. 3. 3. 18:39ㆍStudy/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 |