오늘 알아볼 것은 상속 개념이다.
사실 C++기초를 공부했던게 워낙 옛날이라서 프로젝트를 진행하면서 상속에 대한 개념이 조금 부족했다. 그래서 다시한번 상기시켜보자는 의미로 정리를 한번 해보려고 한다.
상속이란?
- 한 클래스가 다른 클래스에서 정의된 속성(변수, 함수)를 이어받아 사용
- 이미 정의된 클래스를 바탕으로 필요한 기능을 추가하여 정의
라고 단순하게 말할 수 있다.
상속의 장점?
- 중복된 코드를 부모에서 구현하여 중복을 제거할 수 있다.
- 상속을 통해 자식 클래스에서 부모가 가지고 있지 않은 기능을 구현할 수 있다.
- 다형성을 활용한 객체 지향적 디자인
다형성이란?
- 하나의 객체가 여러 가지 타입을 가질 수 있는 것을 의미함.
이 다형성이라는게 사실 처음에 C++을 공부하면서 도대체 다형성이 뭐지? 라는 생각밖에 안들었다. 이건 코드로 설명하는게 이해가 잘 갈 것이다. 본인이 이번 API 프로젝트를 하면서 짠 코드로 설명하도록 하겠다.
우선, 나는 Scene이라는 클래스를 만들고, Scene을 상속받는 여러 Scene을 구현했다. 구조는 다음과 같다.
Main ~ Ending Scene까지는 구현한게 많았기 때문에 속성을 표시하지 않았다.
일단 이렇게 상속을 시켜놓았다. 그리고 Scene을 다루는 SceneManager에서 내가 어떻게 코드를 사용하였는지 보자.
Scene을 여러 개 가질 수 있도록 _scenes를 선언했다. 여기서 Scene은 추상 클래스이므로 무조건 포인터로 만들어야 한다. 그리고 _curSceneIdx는 현재 내가 사용하고 있는 Scene의 인덱스 값이다. 눈치 빠른 사람이면 어떤 부분에서 내가 다형성을 설명할 지 대충 예상할 수 있을 것이다.
우선 SceneManager에서 Init함수를 보면, 난 _scenes의 각 인덱스별로 Scene이 아닌, Scene을 상속받은 클래스로 초기화하였다. Upcasting을 사용. 바로 이게 다형성이다.
난 분명히 Scene의 배열로 _scenes를 선언했지만, 초기화는 저런식으로 자식클래스로 할 수 있다. 이렇게 하면 뭐가 좋은가?
만약 내가 상속개념을 사용하지 않았다면, 저 SceneManager의 코드가 저렇게 간단하게 구현될 수 없었을 것이다. 다형성을 이용하면 굉장히 심플하게 코드를 구현할 수 있다. Draw, Input, Init, ResetScene은 각 클래스에서 모두 다르게 구현되겠지만, 상관없다. 나는 Scene을 추상클래스로 선언하였다.
이렇게 사용하면, 부모는 사실상 구현할게 없다. 이런 추상클래스는 C#에서는 Interface라는 개념으로 사용하지만, C++에서는 그냥 추상클래스로 만든다. 이렇게 추상클래스를 상속받았을 경우, 추상클래스의 함수에 대해 구현할 때는 override 해주어야 한다. 여기서 override란? 가상함수를 자식 클래스에서 재정의하는 것을 말한다. 가상함수 뒤에 override를 붙여주면 된다. 그리고 소멸자의 경우 부모, 자식 모두 무조건 virtual 키워드를 붙여주어야 한다. 소멸자에 virtual 키워드를 붙여주지 않으면, 자식을 소멸할 때 부모객체만을 소멸시키기 때문에 무조건 붙여주어야 한다. 안 그러면 memory leak이 발생한다. 프로젝트하면서 깨달은 사실이 이거였다. 그래서 자식 클래스의 경우 다음과 같이 구현해주면 된다.
virtual 함수에 대해 override 키워드를 붙여주었다.
가상함수에 대해 자세히 알아보자.
https://www.youtube.com/watch?v=NNh72TwpdBA
이 강의를 들으면 이해하기 좋을 것이다.
virtual로 함수를 선언을 하면 해당 클래스에서는 가상함수 테이블을 가리키는 포인터(8byte)를 할당한다. 그리고 해당 함수를 호출하면 가상함수 테이블에서 해당 함수를 불러서 호출한다. 클래스에 가상함수가 포함되면 가상함수 테이블이 생성되고, 이 테이블을 참조하여 호출된 함수가 결정되기 때문에 C에서의 함수호출보다 실행속도가 느리다. 하지만, 다형성이라는 큰 장점을 얻을 수 있기 때문에 많이 사용된다. 이런 가상함수를 동적 바인딩이라고도 부른다. 함수의 실행코드의 결정이 개발 시에 이루어지는 정적바인딩에 반대되는 개념으로, 실행 시에 함수의 실행 코드의 결정이 일어난다는 것이다. 그리고 아까 위에서 순수가상함수를 선언했는데, virtual void Draw() = 0 과 같이 = 0을 붙여주면 순수 가상 함수가 된다.
오늘 포스팅은 여기까지..
'Modern C++' 카테고리의 다른 글
[C++] Priority Queue - 우선순위 큐 (0) | 2021.08.19 |
---|---|
[C++] Iterator (0) | 2021.08.16 |
[C++] Container - Map (0) | 2021.07.20 |
[C++] Container - List (1) | 2021.07.19 |
[C++] Container - array, vector (0) | 2021.07.18 |