Entity* p1 = new Entity{0,0};
Entity* p2 = new Player{0,0,2};
Entity* p3 = new Enemy{0,0,2}
Entity* p4 = new Boss{0,0,2};
p1 -> Move(1,1); // Entity :: Move()
p2 -> Move(1,1); // Player
p3 -> Move(1,1); // Enemy
p4 -> Move(1,1); // Boss
위와 같이 Base class Pointer는 모든 Derived class를 Point 할 수 있다.
이때 Base class가 virtual로 선언이 되고 Derived class에서 overriding이 되어있다면 실제 메모리에 올라간 객체의 Type을 기준으로 함수가 호출이 된다.
그렇다면 우리는 이제 위의 식을 아주 간단하게 표현이 가능하다! 배열을 활용해 보자
/*Entity* p1 = new Entity{0,0};
Entity* p2 = new Player{0,0,2};
Entity* p3 = new Enemy{0,0,2}
Entity* p4 = new Boss{0,0,2};
*/
Entity* arr[] = {p1,p2,p3,p4};
for(int i = 0 ; i < 4; i++ )
arr[i] -> Move(1,1);
이제 동적 바인딩을 사용하여 각 Derived class의 overloading된 함수가 호출된다
Entity* e = new Player{ 1,1,10,10 };
e->Move(2, 2);
e->PrintPosition();
delete e;
Player p{ 3,3,20,20 };
Entity& ref = p;
ref.Move(2, 2);
ref.PrintPosition();
결과를 확인해보면 동일하게 Player class 안의 Move가 호출이 된다.
기본 클래스의 가상 함수는 오버라이드 가능하다.
오버라이드를 위해서는 함수의 원형과 반환형이 동일해야 한다.
CPP 11부터 override 지정자 기능을 제공하여 오버라이딩시 실수를 방지하고, 코드의 가독성을 상승 가능
#include <iostream>
class Entity
{
protected:
int x, y;
public:
Entity(int x, int y)
:x{ x }, y{ y } {}
virtual ~Entity()
{
std::cout << "Entity 소멸자 호출됨" << std::endl;
}
virtual void Move(int dx, int dy)//가상 함수 ,유도 클래스에서 오버라이딩 가능
{
x += dx;
y += dy;
}
virtual void PrintPosition() const
{
std::cout <<"Entity : " << 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 ~Player()
{
std::cout << "Player 소멸자 호출됨" << std::endl;
}
virtual void Move(int dx, int dy)
{
x += dx * 2;
y += dy * 2;
}
virtual void PrintPosition()
{
std::cout << "Player : " << x << "," << y << std::endl;
}
};
int main()
{
Player p{ 1,1,10,10 };
const Entity& e = p;
e.PrintPosition();
}
코드를 보면 const Entity& e = p;에서 const가 붙여진 것을 볼 수 있다. 이때 오류를 피하기 위해서는 Player Class안의 PrintPosition()의 끝에 const를 붙여야 하는 것을 알 수 있다.
우리는 overriding을 통한 동적 바인딩을 구현하고 있지만 위의 코드를 실행해 보면 아래와 같은 결과가 나타난다.
#include <iostream>
class Entity
{
protected:
int x, y;
public:
Entity(int x, int y)
:x{ x }, y{ y } {}
virtual ~Entity()
{
std::cout << "Entity 소멸자 호출됨" << std::endl;
}
virtual void Move(int dx, int dy)//가상 함수 ,유도 클래스에서 오버라이딩 가능
{
x += dx;
y += dy;
}
virtual void PrintPosition() const//const 추가
{
std::cout <<"Entity : " << 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 ~Player()
{
std::cout << "Player 소멸자 호출됨" << std::endl;
}
virtual void Move(int dx, int dy)
{
x += dx * 2;
y += dy * 2;
}
virtual void PrintPosition() const//overriding을 위한 const 추가
{
std::cout << "Player : " << x << "," << y << std::endl;
}
};
int main()
{
Player p{ 1,1,10,10 };
const Entity& e = p;
e.PrintPosition();
}
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 ~Player()
{
std::cout << "Player 소멸자 호출됨" << std::endl;
}
virtual void Move(int dx, int dy)
{
x += dx * 2;
y += dy * 2;
}
virtual void PrintPosition() const override
{
std::cout << "Player : " << x << "," << y << std::endl;
}
};
위와 같이 override 지정자로 명시하여 코드의 가독성을 높인다.
CPP 11 부터 제공됨
Class의 final
class Base final{
//Base 내용
};
class Derived : public Base{ //ERROR 발생
//
};멤버 함수의 final
유도 클래스에서 가상 함수를 오버라이드 하지 못하도록 함
class A{
public:
virtual void do_something();
};
class B : public A{
public:
virtual void do_something() override final;// 오버라이드 OK!
};
class C : public B {
public:
virtual void do_something() override;//ERROR 발생
};
위의 코드를 설명해보면 A를 상속받은 B에서의 do_something의 오버라이드는 가능하고 이때 final 지정자로 더 이상 override를 할 수 없게 했기 때문에 C에서의 do_something()의 override는 불가능하다