안녕하세요. BlockDMask 입니다.
오늘은 C++의 아주 중요한 가상함수, 순수가상함수에 대해서 알아보겠습니다.
우선, 기본적으로 가상함수(virtual)와 순수가상함수(pure virtual)에 대한 기본적인 지식이 조금 있는 분 이셔야 이 내용을 읽기 적절할 듯 싶습니다.
1. 일반(단순)가상함수, 순수가상함수에 대한 기본적인 개념
- 순수가상함수와 가상함수를 아시는 분이라면 문장하나로 이해할 수 있을거라 생각합니다.
- 순수 가상 함수는 인터페이스(Interface)를 자식 클래스에게 전달하기 위해 사용하는 함수입니다.
- 일반(단순) 가상 함수는 인터페이스(Interface) + 함수의 선언(내부 구현) 까지 자식 클래스에게 전달하기 위해 사용하는 함수입니다.
풀어서 말하면,
순수가상함수는 자식에게 '아들아 너는 A기능이 꼭 있어야해! 근데 그것은 너가 알아서 선언해!, 이거 선언안하면 아들아 너의 클래스는 돌아가지 않는단다.'
일반(단순)가상함수는 '아들아 B기능을 물려줄건데, 니가 선언안해도 내가 기본적으로 기능이 돌아가게 해줄게' 이런것입니다.
2. 이럴때 순수가상함수를 쓰고, 이럴때는 일반(단순)가상함수를 사용해보세요!
- 저도 설계를 잘 못하는 입장에서 이래라 저래라 할 자격은 안되지만, 제가 생각하는 설계를 한번 해보겠습니다.
제가 요즘 차에 관심이 부쩍 많아져서, 자동차(부모클래스)로 예를 들어보겠습니다.
- 자동차의 필수조건이 무엇일까요 : 문이 열려야한다, 문이 닫혀야한다, 앞으로 가야한다, 뒤로가야한다, 멈춰야한다.
이렇게 다섯개가 있다고 가정을 해보겠습니다.
이제 자동차 부모클래스를 상속받는 것이 슈퍼카 클래스와 일반카 클래스가 있다고 가정을 해보겠습니다.
문이 위로 열리는 슈퍼카의 종류랑 일반적으로 문이 옆으로 열리는 자동차 이렇게 나눌수 있겠죠?
차이점 : 문이 열리는 방향이 다르다. (아니 물론 퍼포먼스도 다르겠지만..) 입니다.
- 문이 열리는것, 닫히는것은 자동차에 필수적으로 있어야하지만(정의), 열리는 방법 자체(선언)이 다르게 나와야하는것이 느낌이 좀 오시나요?
그러면 우리가 무엇을 가상함수로 구현하고 무엇을 순수가상함수로 설계해야할지 코드로 직접 보겠습니다.
부모클래스의 코드 설계
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 | #include<iostream> using namespace std; class Car { public: virtual void Drive() { //앞으로 간다. cout << "Drive()" << endl; return; } virtual void Stop() { //멈춘다 cout << "Stop()" << endl; return; } virtual void Back() { //후진한다. cout << "Back()" << endl; } //자동차는 문이 열리고 닫혀야한다. //근데 문이 위로 열리는지 옆으로 열리는지 모르니까 //상속받아서 해당 클래스에 맞게 선언, 구현해라. //근데, 이 기능이 없으면 자동차가 아니다. virtual void OpenDoor() = 0; virtual void CloseDoor() = 0; }; | cs |
부모 클래스를 보면, OpenDoor() CloseDoor() 함수는 순수가상함수로 정의해놓은것을 볼 수 있습니다.
이를 통해 Car 클래스를 상속받는 모든 클래스는 무조건 OpenDoor, CloseDoor 함수의 선언(내부구현)을 꼭 만들어서 사용해야하는 것을 알 수 있습니다.
슈퍼카 클래스의 코드 설계
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | class SuperCar : public Car { public: virtual void OpenDoor() override { //위로 열린다. cout << "super - open()" << endl; return; } virtual void CloseDoor() override { //아래로 닫힌다. cout << "super - close()" << endl; return; } }; | cs |
일반카 클래스의 코드 설계
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | class NormalCar : public Car { public: virtual void OpenDoor() override { //옆으로 열린다. cout << "normal - open()" << endl; return; } virtual void CloseDoor() override { //옆으로 닫힌다. cout << "normal - close()" << endl; return; } }; | cs |
Main에서 사용 예시
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 | int main(void) { cout << "1> 슈퍼카 클래스의 Drive와 OpenDoor" << endl; SuperCar* pSuper = new SuperCar; pSuper->Drive(); pSuper->OpenDoor(); cout << "2> 일반카 클래스의 Drive와 OpenDoor" << endl; NormalCar* pNormal = new NormalCar; pNormal->Drive(); pNormal->OpenDoor(); cout << "3> Car 부모 클래스의 포인터로 자식들의 객체를 가리킨다." << endl; Car* pCar = nullptr; pCar = dynamic_cast<Car*>(pSuper); pCar->Drive(); pCar->OpenDoor(); pCar = dynamic_cast<Car*>(pNormal); pCar->Drive(); pCar->OpenDoor(); system("pause"); pCar = nullptr; delete pSuper; delete pNormal; return 0; } | cs |
>>결과
- 생성자와 소멸자는 일부러 작성하지 않았습니다.
- 업캐스팅에도 dynamic_cast를 사용했습니다. dynamic_cast 관련 글은 [여기] 에 있습니다.
- OpenDoor, CloseDoor 함수가 순수 가상함수가 꼭 아니어도 됩니다. 이것은 설계의 스타일? 본인의 판단입니다. 순수가상함수가 아니고 일반 가상함수로 만들어서 오버라이팅 하면 되기도 합니다만, 해당 페이지의 주제는 순수가상함수와 가상함수의 쓰임에 대해서 구분을 하려는 목적이었으니, 적절하지 않다고 판단되어도 너그럽게 넘어가주시길 바라겠습니다.
- 이상 "가상함수와 순수가상함수의 차이(상속)에 대해서" 포스팅을 마치겠습니다. 감사합니다.
'<개인공부> > [C++]' 카테고리의 다른 글
[C++] to_string 함수에 대해서 (int to string) (4) | 2019.03.17 |
---|---|
[C++] stoi, stof, stol, stod 함수에 대해서 (string to int) (4) | 2019.03.16 |
[C++] array container 정리 및 사용법 (std::array) (4) | 2019.03.15 |
[C++] new, delete 동적할당과 해제에 대해서 (0) | 2018.12.04 |
[C++] dynamic_cast (타입캐스트 연산자) (1) | 2018.07.25 |
[C++] reinterpret_cast (타입캐스트 연산자) (5) | 2017.11.29 |
[C++] const_cast (타입 캐스트 연산자) (2) | 2017.11.28 |
[C++] static_cast (타입캐스트 연산자) (4) | 2017.11.28 |