C++ Chapter 12.1 : 다형성의 기본 개념
카테고리: Cpp
태그: Cpp Programming
인프런에 있는 홍정모 교수님의 홍정모의 따라 하며 배우는 C++ 강의를 듣고 정리한 필기입니다. 😀
🌜 [홍정모의 따라 하며 배우는 C++]강의 들으러 가기!
chapter 12. 가상 함수들 : 다형성의 기본 개념
🔔 다형성 활용하기 전
부모 타입의 포인터 딱 하나로 여러 자식들의 멤버를 호출할 수 있는 것
class Animal
{
protected:
string m_name;
public:
Animal(std::string name)
: m_name(name)
{}
public:
string getName() { return m_name; }
void speak() const
{
cout << m_name << " ??? " << endl;
}
};
class Cat : public Animal
{
public:
Cat(string name)
: Animal(name)
{}
void speak() const
{
cout << m_name << "Meow " << endl;
}
};
class Dog : public Animal
{
public:
Dog(string name)
: Animal(name)
{}
void speak() const
{
cout << m_name << "Woof " << endl;
}
};
- 부모 클래스 : Animal
- 자식 클래스 1 : Dog
- 자식 클래스 2 : Cat
- Animal은 void speark() const 멤버 함수를 가지고 있는데
- Dog, Cat 은 각각 speak() 함수를 재정의(오버라이딩)한 상태다.
int main()
{
Animal animal("my animal");
Cat cat("my cat");
Dog dog("my dog");
animal.speak(); // "my animal ???" 출력
cat.speak(); // "my cat Meow" 출력
dog.speak(); // "my dog Woof" 출력
Animal *ptr_animal1 = &cat;
Animal *ptr_animal2 = &dog;
ptr_animal1->speak(); // "my cat ???" 출력
ptr_animal2->speak(); // "my dog ???" 출력
return 0;
}
C++은 참조하는 대상의 타입이 아닌 멤버를 호출하는 변수(포인터 혹은 객체변수)의 타입에 따라 그 타입에 맞는 멤버를 호출한다.
animal.speak(); // "my animal ???" 출력
cat.speak(); // "my cat Meow" 출력
dog.speak(); // "my dog Woof" 출력
- animal.speak() 👉 Animal의 speak() 멤버 함수 호출
- cat.speak() 👉 Cat의 speak() 멤버 함수 호출
- dog.speak() 👉 Dog의 speak() 멤버 함수 호출
Animal *ptr_animal1 = &cat;
Animal *ptr_animal2 = &dog;
ptr_animal1->speak(); // "my cat ???" 출력
ptr_animal2->speak(); // "my dog ???" 출력
- ptr_animal1과 ptr_animal2은
Animal
타입이다. - ptr_animal1은 Cat타입 객체의 주소를 담고 있지만 ptr_animal1 자체는 Animal 타입이다.
- Cat타입 객체의 주소를 담고 있음에도 불구하고 C++은 포인터 변수, 호출하는 변수의 타입을 더 중요시 하기 때문에
- ptr_animal1->speak(); 👉 Animal의 speak() 멤버 함수 호출
- Cat타입 객체의 주소를 담고 있음에도 불구하고 C++은 포인터 변수, 호출하는 변수의 타입을 더 중요시 하기 때문에
- ptr_animal2은 Dog타입 객체의 주소를 담고 있지만 ptr_animal2 자체는 Animal 타입이다.
- Dog타입 객체의 주소를 담고 있음에도 불구하고 C++은 포인터 변수, 호출하는 변수의 타입을 더 중요시 하기 때문에
- ptr_animal2->speak(); 👉 Animal의 speak() 멤버 함수 호출
- Dog타입 객체의 주소를 담고 있음에도 불구하고 C++은 포인터 변수, 호출하는 변수의 타입을 더 중요시 하기 때문에
🔔 다형성을 사용하는 이유
cat.speak()
dog.speak()
rabbit.speak()
pig.speak()
cow.speak()
lion.speak()
...
- Animal의 자식의 종류가 위와같이 여러가지라면 다형성을 사용하지 않을시 자식마다 일일이 자식타입의 변수로 멤버 함수를 호출해주어야 하는 번거로움이 따른다.
Animal * animals[] = { &cat, &dog, &pig, &lion };
for(int i = 0; i < 4; i++)
{
animals[i]->speak();
}
💎출력💎
my cat ???
my dog ???
my pig ???
my lion ???
- 모든 자식들은 Animal을 상속 받았기 때문에 speak()를 공통적으로 가지고 있는 상태이므로
- 위와 같이 부모 타입의 포인터 컨테이너에 여러가지 자식들을 넣어준 후 for문을 돌려 animals[i]->speak() 딱 한 문장으로 각각 본인이 오버라이딩 한 *speak()*를 호출하게 할 순 없을까?.
- 이걸 가능하게 하려면 speak() 함수를 Animal 클래스에서 가상 함수로 지정해놔야 한다.
virtual
가상 함수
부모 타입 포인터로 자식 객체를 가리킬시 자식이 오버라이딩 한 것을 호출하게 하고 싶은 멤버 함수를
virtual
가상 함수로 지정하면 된다.
class Animal
{
protected:
string m_name;
public:
Animal(std::string name)
: m_name(name)
{}
public:
string getName() { return m_name; }
virtual void speak() const
{
cout << m_name << " ??? " << endl;
}
};
virtual void speak() const
- 멤버 함수 맨 앞에
virtual
을 붙여주면 가상 함수로 선언된다.- 👉 부모 타입의 포인터로 호출하더라도 자식 클래스가 오버라이딩 한 함수를 호출하게끔 한다.
- 이 때문에 자식 클래스에서 가상 함수를 오버라이딩 하기를 권장한다.
- (필수 사항은 아니다. 오버라이딩이 필수 사항인건 순수 가상함수)
- 자식 클래스에서 오버라이딩 되있지 않으면 부모가 정의해놓은 부모타입의 가상 함수가 호출된다.
- 자식 클래스에서 오버라이딩 하지 않으면 cout « m_name « ” ??? “ « endl; 를 실행할 것.
- 이 때문에 자식 클래스에서 가상 함수를 오버라이딩 하기를 권장한다.
- 👉 부모 타입의 포인터로 호출하더라도 자식 클래스가 오버라이딩 한 함수를 호출하게끔 한다.
Animal * animals[] = { &cat, &dog, &pig, &lion };
for(int i = 0; i < 4; i++)
{
animals[i]->speak();
}
💎출력💎
my cat 냐옹
my dog 멍멍
my pig 꿀꿀
my lion 어흥
Animal 부모클래스의 speak() 함수를 가상 함수로 지정하니 부모 타입의 포인터 한줄로 호출했음에도 불구하고 참조하는 자식 객체 타입에 따라 각각 자식들이 오버라이딩한 speak 함수를 호출하고 있는 것을 볼 수 있다.
🌜 개인 공부 기록용 블로그입니다. 오류나 틀린 부분이 있을 경우
언제든지 댓글 혹은 메일로 지적해주시면 감사하겠습니다! 😄
댓글 남기기