C++ Chapter 12.1 : 다형성의 기본 개념

Date:     Updated:

카테고리:

태그:

인프런에 있는 홍정모 교수님의 홍정모의 따라 하며 배우는 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
  • Animalvoid 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() 👉 Animalspeak() 멤버 함수 호출
  • cat.speak() 👉 Catspeak() 멤버 함수 호출
  • dog.speak() 👉 Dogspeak() 멤버 함수 호출
	Animal *ptr_animal1 = &cat;
	Animal *ptr_animal2 = &dog;

	ptr_animal1->speak();  // "my cat ???" 출력
	ptr_animal2->speak();  // "my dog ???" 출력
  • ptr_animal1ptr_animal2Animal타입이다.
  • ptr_animal1Cat타입 객체의 주소를 담고 있지만 ptr_animal1 자체는 Animal 타입이다.
    • Cat타입 객체의 주소를 담고 있음에도 불구하고 C++은 포인터 변수, 호출하는 변수의 타입을 더 중요시 하기 때문에
      • ptr_animal1->speak(); 👉 Animalspeak() 멤버 함수 호출
  • ptr_animal2Dog타입 객체의 주소를 담고 있지만 ptr_animal2 자체는 Animal 타입이다.
    • Dog타입 객체의 주소를 담고 있음에도 불구하고 C++은 포인터 변수, 호출하는 변수의 타입을 더 중요시 하기 때문에
      • ptr_animal2->speak(); 👉 Animalspeak() 멤버 함수 호출


🔔 다형성을 사용하는 이유

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 함수를 호출하고 있는 것을 볼 수 있다.



🌜 개인 공부 기록용 블로그입니다. 오류나 틀린 부분이 있을 경우 
언제든지 댓글 혹은 메일로 지적해주시면 감사하겠습니다! 😄

맨 위로 이동하기

Cpp 카테고리 내 다른 글 보러가기

댓글 남기기