[C++] 2.3 다형성으로 유연하게

Date:     Updated:

카테고리:

태그:

인프런에 있는 홍정모 교수님의 홍정모의 게임 만들기 연습 문제 패키지 강의를 듣고 정리한 필기입니다.😀
🌜 공부에 사용된 홍정모 교수님의 코드들 보러가기
🌜 [홍정모의 게임 만들기 연습 문제 패키지] 강의 들으러 가기!


Chapter 2. 객체 지향으로 가는 길 : 다형성으로 유연하게

🔔 부모 포인터로 자식 객체 참조하기

  • 자식 객체 중에서 부모로부터 상속받은 함수나 변수부모 포인터로 접근할 수 있다.
    GeometricObject * my_obj = &my_tri;
    my_obj->draw();
    
    • 부모 타입의 my_obj가 Triangle 타입의 자식 객체인 my_tri의 주소를 가리키는 포인터가 된다.
    • my_obj->draw();
      • Triangle 타입의 자식을 참조하고 있기 때문에 Triangle의 draw()가 호출된다.
  • 단 C++에서는 가상 함수가 아니라면 부모 포인터로 자식 객체를 참조하더라도 무조건 부모 함수를 호출 한다.


부모 타입의 포인터들을 벡터에 담아 관리해보자.

  • ✨ vector 의 원소 타입을 부모 타입의 포인터로 지정하면 여러 타입의 자식 객체를 접근 할 수 있다.
    • Geometric * 타입 포인터 하나로 &my_tri, &my_box, &my_cir 다 참조 가능.
      • 이런게 바로 다형성. 부모 포인터 하나로 여러가지 자식을 다 참조할 수 있다.
    • 각 자식 객체들의 포인터를 부모 타입 포인터로 참조하는 상태로 벡터에 넣어주기
  • 부모 타입 포인터들을 담은 벡터 한번만 for문으로 순회하면 여러 타입의 자식 객체들 전부 draw() 시킬 수 있음.
std::vector<GeometricObject *> my_objs; // 부모 타입의 포인터들을 담는 벡터

my_objs.push_back(&my_tri);   
my_objs.push_back(&my_cir);
my_objs.push_back(&my_box);

for (const auto & obj : my_objs)   
	obj->draw();

자식 객체를 동적 할당으로 생성

  • 그냥 Triangle my_tri 는 범위를 벗어나 사라지면 update()에서 draw시 접근 못하게 디니까.
    • new로 동적할당 받으면 delete하기 전까지는 힙메모리에 계속 존재해서 사용 가능.
  • 이제 my_tri.init 가 아닌 my_tri->init
  • auto 타입의 포인터로 선언.
    • 나중에 부모 타입의 포인터에 옮길 것
class Example : public Game2D
	{
	public:

		std::vector<GeometricObject *> my_objs;

		Example()
			: Game2D()
		{
			auto *my_tri = new Triangle; // new 로 동적할당 후 주소 리턴
			auto *my_cir = new Circle; // new 로 동적할당 후 주소 리턴
			auto *my_box = new Box; // new 로 동적할당 후 주소 리턴

			my_tri->init(Colors::gold, vec2{ -0.5f, 0.2f }, 0.1f);
			my_cir->init(Colors::olive, vec2{ 0.1f, 0.1f }, 0.1f);
			my_box->init(Colors::black, vec2{ 0.5f, 0.5f }, 0.2f, 0.1f);
		
			my_objs.push_back(my_tri);
			my_objs.push_back(my_cir);
			my_objs.push_back(my_box);
		}

		void update() override
		{
			for (const auto & obj : my_objs)
				obj->draw();
		}
	};


🔔 생성자를 이용하여 간결하게 초기화 하기

생성자에서 초기화를 해주면 객체 생성과 동시에 초기화가 가능하다. 생성자 안에서 init 함수 호출!

Triangle(const RGB& _color, const vec2& _pos, const float& _size)
{
	init(_color, _pos, _size);
}

void init(const RGB& _color, const vec2& _pos, const float& _size)
{
	GeometricObject::init(_color, _pos);
			size = _size;
}
  • init 초기화 함수를 따로 두고 생성자 안에서 init을 이용하는 식.
  • 객체 생성과는 별개로 하나 하나 자식객체마다 init 함수 써서 초기화 할 필요가 없어졌다.
  • 이런 과정에서 upgrade 함수의 draw는 건들일 필요가 없다.

📜Box.h


Box()
{}
		
Box(const RGB& _color, const vec2& _pos, const float& _width, const float& _height)  // 생성자
{
	init(_color, _pos, _width, _height);
}

void init(const RGB& _color, const vec2& _pos, const float& _width, const float& _height)
{
	GeometricObject::init(_color, _pos);

	width = _width;
	height = _height;
}

📜main

auto *my_tri = new Triangle(Colors::gold, vec2{ -0.5f, 0.2f }, 0.1f);
auto *my_cir = new Circle(Colors::olive, vec2{ 0.1f, 0.1f }, 0.1f);
auto *my_box = new Box(Colors::black, vec2{ 0.5f, 0.5f }, 0.2f, 0.1f);


🔔 가상 소멸자

GeometricObject * my_obj = &my_tri;
delete my_obj;  // 부모 소멸자가 호출된다.

부모 포인터로 자식 객체를 참조할 때, 부포 포인터를 delete시키면 자식 객체를 참조하고 있음에도 불구하고 부모 소멸자를 호출한다 C++은 포인터가 어떤 타입이냐를 더 중요하게 따지기 때문에.

가상 소멸자 : 자식 클래스의 소멸자가 실행 되도록 한다.

부모 클래스의 소멸자 선언 앞에 virtual을 붙여주면 자식 클래스의 소멸자가 호출된다.

// 부모 클래스 GeometricObject

virtual ~GeometricObject()
{}
  • cf) 스마트 포인터
    • 부모 포인터를 스마트 포인터로 사용하면 동적 할당을 하더라도 delete을 따로 해줄 필요가 없다.
    • 또한 스마트 포인터 중 shared_ptr는 부모 소멸자에 virtual을 붙이지 않아도 자동으로 자식 소멸자가 호출된다.


🌜 개인 공부 기록용 블로그입니다. 오류나 틀린 부분이 있을 경우 
언제든지 댓글 혹은 메일로 지적해주시면 감사하겠습니다! 😄

맨 위로 이동하기

C++ games 카테고리 내 다른 글 보러가기

댓글 남기기