프로그래밍 언어/C++

C++(17) 다형성 - 1

KJ1111 2023. 9. 15. 13:52
728x90

다형성에서 중요한 부분은 동적 바인딩이다. 이전에 정적 바인딩은 Compile-time 에서 어떤 함수를 호출할 지 정하는 것을 정적 바인딩이라고 한다. 동적 바인딩은 Run-time에서 어떤 함수를 실행하는지 정하는 것이다. 런타임 다형성은 런타임에서 같은 함수에 대해 다른 의미를 부여하는 것이고 이것을 함수의 오버라이딩이라고 한다. 동적 바인딩의 조건은 3가지 있다.

  • 상속
  • 기본 클래스 포인터 또는 참조자
  • 가상 함수

정적 바인딩은 컴파일시 타입을 기준으로 호출 함수를 결정하지만, 동적 바인딩은 런타임시 실제 메모리에 저장된 타입을 기준으로 호출 함수를 결정한다. 

 

유도 클래스에서 기본 클래스의 함수를 재정의 또는 오버라이드해 사용할 수 있다. 오버라이드된 함수는 동적 바인딩을 통해 활용 가능하다. 오버라이드 될 수 있는 함수를 가상함수라고 한다.

#include <iostream>

class Entity
{
protected:
    int x, y;
public:
    Entity(int x, int y)
        : x{ x }, y{ y } {}
    virtual void Move(int dx, int dy)
    {
        x += dx;
        y += dy;
    }
    void PrintPosition()
    {
        std::cout << x << "," << y << std::endl;
    }
};

class Player : public Entity
{
private:
    int hp;
    int xp;
public:
    Player(int x, int y, int hp, int xp)
        : Entity{ x, y }, hp{ hp }, xp{ xp } {}
    virtual void Move(int dx, int dy)
    {
        x += dx * 2;
        y += dy * 2;
    }
};
int main()
{
    Entity* e = new Player{ 1, 1, 10, 10 };
    e->PrintPosition();
    
    e->Move(2, 1);
    e->PrintPosition();
	
    delete e;
    return 0;
}

 가상함수를 만들려면, 기본 클래스의 함수에 virtual 키워드를 앞에 작성한다. 유도 클래스에서 가상함수로 만들려는 함수에 virtual 키워드르 이 함수가 붙이면 오버라이드 된 함수임을 좀더 보기 명확해 진다. 만약 유도 클래스에서 함수를 오버라이드 하지 않을 경우 기본 클래스의 함수가 상속된다. 

#include <iostream>

class Entity
{
protected:
    int x, y;
public:
    Entity(int x, int y)
        : x{ x }, y{ y } {}
    ~Entity()
    {
        std::cout << "Entity Destructor Called" << std::endl;
    }
    virtual void Move(int dx, int dy)
    {
        x += dx;
        y += dy;
    }
    void PrintPosition()
    {
        std::cout << x << "," << y << std::endl;
    }
};

class Player : public Entity
{
private:
    int hp;
    int xp;
public:
    Player(int x, int y, int hp, int xp)
        : Entity{ x, y }, hp{ hp }, xp{ xp } {}
    ~Player()
    {
        std::cout << "Player Destructor Called" << std::endl;
    }
    virtual void Move(int dx, int dy)
    {
        x += dx * 2;
        y += dy * 2;
    }
};
int main()
{
    Entity* e = new Player{ 1, 1, 10, 10 };
    e->PrintPosition();
    
    e->Move(2, 1);
    e->PrintPosition();

    delete e;
    return 0;
}
// 출력
1,1
5,3
Entity Destructor Called

만약 기본 클래스의 소멸자를 가상함수로 지정하지 않을 경우 Entity의 소멸자만 호출된다.

virtual ~Entity()
{
    std::cout << "Entity Destructor Called" << std::endl;
}
// 출력
1,1
5,3
Player Destructor Called
Entity Destructor Called

클래스가 가상함수를 가지면, 항상 가상 소멸자를 함께 정의해야 한다.