본문 바로가기

강의자료/Class

007. 클래스의 꽃이라 불리우는 다형성을 이용하자.

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


지금까지 배운 클래스에 대한 개념은 그저 형태뿐이었다.
여기서 배울 다형성을 이용해야만이 객체지향적인 프로그래밍을 하고 있다고 자신있게 말할 수 있는 것이다.
이전 글에서 가상함수에 대해서 완벽히 이해를 했다면 다형성을 이해하는 것도 그다지 어렵지 않다.
아래 코드를 보고 다형성에 대해서 이해하고 다형성이 왜 클래스의 꽃이라고 불리우는지 느껴보자.
// 다형성이란, 하나의 객체가 다양한 형태의 특성을 가진다는 의미이다.
// 다형성은 (1) 부모 클래스의 포인터는 자식 클래스의 인스턴스를 가리킬 수 있다는 특성과 (2)가상함수를 이용하여 부모 클래스의 포인터를 통해 자식 클래스의 함수를 호출할 수 있다는 특성을 모두 활용하여 구현하게 된다.
// 다형성에서의 부모 클래스는 일반적인 상속관계에서처럼 자식 클래스가 가지는 공통 기능을 위한 존재가 아니라 단지, 자식 클래스들이 어떠한 기능을 가지고 있는지를 정의하는 껍데기에 불과하다.
// 다양한 형태의 자식 클래스들을 만들고 이들 함수의 이름을 모두 통일시키면 부모 클래스의 포인터 하나로 모든 자식 클래스의 기능을 사용할 수 있게 된다. 이게 바로 한가지 타입의 변수로 다양한 기능을 가진 객체를 구현하는 다형성의 원리이다.

// 아래 코드를 다형성을 이용하지 않고 구현하려면 자식 클래스의 종류별로 배열을 따로 만들어서 처리해야 하지만, 아래와 같이 다형성을 이용하여 구현하면 하나의 배열로 모두 처리할 수 있게 된다.


#include 
using namespace std;

class Mammal
{
public:
	Mammal():itsAge(1) { }
	virtual ~Mammal() { }
	virtual void Speak() const 
	{ 
		cout << "Mammal speak!\n"; 
	}
protected:
	int itsAge;
};

class Dog : public Mammal
{
public:
	void Speak() const { cout << "Woof!\n"; }
};

class Cat : public Mammal
{
public:
	void Speak() const { cout << "Meow!\n"; }
};

class Horse : public Mammal
{
public:
	void Speak() const { cout << "Winnie!\n"; }
};

class Pig : public Mammal
{
public:
	void Speak() const { cout << "Oink!\n"; }
};

void main()
{
	// 5개의 클래스(Mammal, Dog, Cat, Horse, Pig)를 만든다.
	// 각각의 클래스는 모두 Speak()함수를 가지고 다음과 같은 소리를 낸다.
	// Mammal : Mammal speak!
	// Dog : Woof!
	// Cat : Meow!
	// Horse : Winnie!
	// Pig : Oink!
	// 배열을 이용하여 5개의 객체를 만들기 위해서 사용자로부터 "(1)dog (2)cat (3)horse (4)pig: "와 같은 입력을 5번 받아 그에 해당하는 객체를 생성한다.
	// 만약, 잘못된 숫자를 입력하면 mammal객체를 생성한다.
	// 5개의 객체를 모두 생성한 후 화면에 해당 객체의 speak()함수를 호출한다.

	// 1. Mammal타입의 포인터 변수 5개를 생성.
	// 2. 사용자로부터 "(1)dog (2)cat (3)horse (4)pig: "와 같은 입력을 5번 받아
	//    5개의 포인터 변수에 번호에 해당하는 객체를 생성.
	//    (1,2,3,4이외의 숫자는 Mammal객체로 생성.)
	// 3. 위에서 생성한 5개의 객체의 Speak()함수를 호출.
	// 4. 위에서 생성한 5개의 객체를 소멸.

	Mammal* theArray[5];

	int choice;
	for ( int i = 0 ; i<5 ; i++ )
	{
		cout << "(1)dog (2)cat (3)horse (4)pig: ";
		cin  >> choice;
		switch (choice)
		{
		case 1: theArray[i] = new Dog;
			break;
		case 2: theArray[i] = new Cat;
			break;
		case 3: theArray[i] = new Horse;
			break;
		case 4: theArray[i] = new Pig;
			break;
		default: theArray[i] = new Mammal;
		}
	}

	for ( int i=0 ; i<5 ; i++ )
		theArray[i]->Speak();

	for ( int i=0 ; i<5 ; i++ )
		delete theArray[i];
}

아래 코드는 다형성을 이용할 때 헷갈릴 수 있는 부분에 대한 예제이다.
보통 다형성을 이용하다 보면 객체를 함수에 전달하거나 다른 변수에 대입하게 될 때, 어떠한 결과가 발생할 지 헷갈릴 수 있다.
아래 코드를 실행해보고 완벽히 이해한다면, 다형성을 이용함에 있어서 헷갈리지 않고 자신있게 코드를 작성할 수 있게 된다.
#include 
using namespace std;

class Mammal
{
public:
	Mammal():itsAge(1) {}
	virtual ~Mammal() {}
	virtual void Speak() const { std::cout << "Mammal speak!\n"; }
protected:
	int itsAge;
};

class Dog : public Mammal
{
public:
	void Speak() const { std::cout << "Woof!\n"; }
};

class Cat : public Mammal
{
public:
	void Speak() const { std::cout << "Meow!\n"; }
};

void ValueFunction (Mammal MammalValue)
{
	MammalValue.Speak();
}
void PtrFunction (Mammal * pMammal)
{
	pMammal->Speak();
}
void RefFunction (Mammal & rMammal)
{
	rMammal.Speak();
}

void main() 
{
	Mammal* ptr = new Cat;

	PtrFunction(ptr);
	RefFunction(*ptr);
	ValueFunction(*ptr);
	delete ptr;

	cout << "-------------------------" << endl;

	Dog star;
	Mammal* ptr2 = &star;
	ptr2->Speak();
	Mammal& refdog = star;
	refdog.Speak();
	Mammal val = star;
	val.Speak();
}