[디딤돌 C++] 31. 다형성 개요

이번에는 OOP의 주요 특징 세 가지인 캡슐화, 상속, 다형성 중에 다형성에 관해 살펴봅시다.

다형성은 기반 형식의 멤버를 파생 형식에서 상속받는 장점을 활용할 수 있는 OOP 특징입니다. 하나의 기반 형식에서 파생한 다양한 파생 클래스가 있을 때 같은 형식의 변수로 접근할 수 있으면 프로그래밍을 효과적으로 할 수 있겠죠.

C++언어에서는 기반 형식의 포인터 변수로 파생 형식의 개체를 설정할 수 있습니다. 그리고 기반 형식의 참조 변수로 파생 형식의 개체를 설정할 수도 있습니다.

class Musician
{
};
class Pianist:public Musician
{
};
int main()
{
    Musician *musician = new Pianist(); //기반 형식 포인터 변수에 파생 형식 개체를 설정
    Pianist pianist;
    Musician &mu = pianist; //기반 형식 참조 변수로 파생 형식 개체를 설정
    delete musician;    
    return 0;
}

예를 들어 음악가 클래스를 기반으로 피아니스트, 드러머 등의 다양한 파생 클래스를 정의하였을 때 밴드를 구성하는 개체들을 음악가 포인터로 관리할 수 있습니다. 이는 각각의 형식 변수로 개체를 참조하는 것에 비교하였을 때 참으로 매력적인 일이 아닐 수 없습니다.

//형식의 다형성
#include <iostream>
using namespace std;
class Musician
{
public:
    void Play()
    {
        cout<<"연주하다."<<endl;
    }
};

class Pianist:public Musician
{

};

class Drummer:public Musician
{

};
class Band
{
    Musician *members[2];
public:
    Band()
    {
        members[0] = new Pianist();
        members[1] = new Drummer();
    }
    void Play()
    {
        for(int i = 0; i<2;i++)
        {
            members[i]->Play();
        }
    }
};


int main()
{
    Band *band = new Band();
    band->Play();
    delete band;
    return 0;
}

▷ 실행 결과

연주하다.

연주하다.

그리고 개체를 설정한 변수 형식이 아닌 실제 개체 형식에 정의한 메서드를 수행할 수 있게 정의할 수 있습니다. 기반 형식에서 메서드를 정의할 때 virtual 키워드를 붙여 정의하면 변수의 형식이 아닌 실제 생성한 개체에 정의한 메서드를 수행합니다. 이를 가상 메서드라 부릅니다.

//메서드의 다형성
#include <iostream>
using namespace std;

class Musician
{
public:
    virtual void Play()//가상 메서드
    {
        cout<<"딩동댕"<<endl;
    }
};

class Pianist:public Musician
{    
};

class Drummer:public Musician
{
public:
    void Play()//재정의
    {
        cout<<"두두둥"<<endl;
    }
};

class Band
{
    Musician *members[2];
public:
    Band()
    {
        members[0] = new Pianist();
        members[1] = new Drummer();
    }
    ~Band()
    {
        for(int i = 0; i<2;i++)
        {
            delete members[i];
        }
    }
    void Play()
    {
        for(int i = 0; i<2;i++)
        {
            members[i]->Play();
        }
    }
};

int main()
{
    Band *band = new Band();
    band->Play();
    delete band;
    return 0;
}

▷실행 결과

딩동댕

두두둥

이처럼 다형성에는 기반 형식의 변수로 파생 형식 개체를 설정하여 사용하는 형식의 다형성과 가상 메서드를 정의하여 실제 개체 형식에 정의한 메서드를 수행하게 하는 메서드의 다형성을 제공합니다.

우리는 여기에서 형식의 다형성과 메서드의 다형성을 다룰 것입니다. 그리고 기반 형식에 정의하지 않은 멤버를 파생 형식에 정의하였을 때 실행 시점에 실제 개체 형식을 참조하여 사용하는 하향 캐스팅에 관해서도 다룰 것입니다.

//메서드의 다형성과 하향 캐스팅
#include <iostream>
using namespace std;

class Musician
{
public:
    virtual void Play()//가상 메서드
    {
        cout<<"딩동댕"<<endl;
    }
};

class Pianist:public Musician
{ 
public:
    void Tuning()
    {
        cout<<"조율하다."<<endl;
    }
};

class Drummer:public Musician
{
public:
    void Play()//재정의
    {
        cout<<"두두둥"<<endl;
    }
};

class Band
{
    Musician *members[2];
public:
    Band()
    {
        members[0] = new Pianist();
        members[1] = new Drummer();
    }

    ~Band()
    {
        for(int i = 0; i<2;i++)
        {
            delete members[i];
        }
    }

    void Play()
    {
        for(int i = 0; i<2;i++)
        {
            Pianist *pianist = dynamic_cast<Pianist *>(members[i]); //실행 시점에 하향 캐스팅
            if(pianist)
            {
                pianist->Tuning();
            }
            members[i]->Play();
        }
    }
};

int main()
{
    Band *band = new Band();
    band->Play();
    delete band;
    return 0;
}

▷ 실행 결과

조율하다.

딩동댕

두두둥

마지막으로 상속과 다형성에 관한 다양한 실습을 해 볼 거예요.