[C++] 2.3 다형성으로 유연하게
카테고리: C++ games
태그: Cpp Graphics OpenGL Programming
인프런에 있는 홍정모 교수님의 홍정모의 게임 만들기 연습 문제 패키지 강의를 듣고 정리한 필기입니다.😀
🌜 공부에 사용된 홍정모 교수님의 코드들 보러가기
🌜 [홍정모의 게임 만들기 연습 문제 패키지] 강의 들으러 가기!
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을 붙이지 않아도 자동으로 자식 소멸자가 호출된다.
- 부모 포인터를
🌜 개인 공부 기록용 블로그입니다. 오류나 틀린 부분이 있을 경우
언제든지 댓글 혹은 메일로 지적해주시면 감사하겠습니다! 😄
댓글 남기기