Base Class Pointer

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가 호출이 된다.

Override 지정자


  • 기본 클래스의 가상 함수는 오버라이드 가능하다.

  • 오버라이드를 위해서는 함수의 원형과 반환형이 동일해야 한다.

    • 만일 다르다면, 오버라이드가 아닌 다른 함수의 정의가 되어버림
    • 다른 함수의 정의는 정적 바인딩
  • CPP 11부터 override 지정자 기능을 제공하여 오버라이딩시 실수를 방지하고, 코드의 가독성상승 가능

    • 어떤 함수가 오버라이딩된 함수인지 정의만 보고도 파악 가능

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을 통한 동적 바인딩을 구현하고 있지만 위의 코드를 실행해 보면 아래와 같은 결과가 나타난다.

  • 이는 overriding한 함수와 virtual 함수가 일치하지 않기 때문에 정적 바인딩이 되어버린 것이다.
  • 즉, 우리는 Player class의 PrintPosition() 끝에 const를 붙여줘야 한다.
  • 아래의 코드가 우리가 원한 결과가 나오게 한다.
#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();

}

수정 결과


  • 우리가 본래에 원하던 결과가 도출이 되었다.
  • 하지만 이는 코드를 작성한 사람은 알 수 있지만 다른 사람이 보면 '오버라이딩 하는 함수구나!’ 라고 알아보기가 힘들다.
  • 즉, override 지정자를 이때 사용한다.

override 지정자 수정 코드

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 지정자로 명시하여 코드의 가독성을 높인다.

final 지정자


  • CPP 11 부터 제공됨

  • Class의 final

    • Class를 더이상 상속하지 못하도록 함
    • 컴파일 에러가 발생한다.
      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는 불가능하다

profile
개발로그

0개의 댓글