[C++] 순수 가상 함수(pure virtual method)와 추상 클래스(abstract class) 연관
클래스를 구현하다보면 동일한 기능을 하지만 내부 구조는 다르게 동작하는 것들이 있다. 예를 들면 일반 내연기관 자동차는 엑셀을 밟을 시 엔진이 회전하고 기름을 소모해 폭발 행정과 변속기를 통해 차량을 이동시키지만 전기차는 전기 모터를 회전시켜 가속을 한다.
사용자가 사용하기에는 두 차량 모두 엑셀 페달을 밟았다는 행동은 동일하다. 사용자는 차의 내부에서 어떤 동작이 일어나는지 구체적으로 알 지 못하더라도 차는 앞으로 간다.
내연기관 자동차와 전기 자동차의 엑셀을 각각 구현해도 괜찮겠지만 만약 두 자동차가 Car라는 클래스를 상속받은 차량이고 Car에 엑셀 페달을 밟는 함수가 없다면 업캐스팅 시에 엑셀레이터를 사용하지 못하는 경우가 발생한다.
따라서 부모 클래스에서 인터페이스를 제공해 자식 클래스에서 그에 맞게 정의하도록 하는 것이 유리하다. 이를 위해 순수 가상 함수를 추가하고 추상클래스를 만든다.
[순수 가상 함수(pure virtual method)]
순수 가상 함수는 클래스의 선언부에서 함수의 정의부가 없다고 명시적으로 설정한 함수이다. 클래스의 멤버 함수를 순수 가상 함수로 만들면 컴파일러는 이 함수의 정의가 현재 클래스에 존재하지 않다고 판단한다.
class Car
{
virtual ~Car() = default;
virtual void Accelerate(float power) = 0;
virtual void Brake(float power) = 0;
};
순수 가상 함수를 만드는 방법은 정의부를 구현하지 않은 채로 함수 선언 뒤에 [= 0;]을 붙여주면 된다. 정의부를 구현하지 않은 채로 = 0; 를 붙이지 않으면 에러가 발생한다.
[추상 클래스(abstract class)]
추상 클래스는 클래스 내에 순수 가상 함수가 1개라도 있는 클래스를 의미한다. 추상 클래스는 함수의 일부가 구현되어 있지 않은 상태이기 때문에 이 클래스로 인스턴스를 생성할 수 없다. 즉, Car *pCar = new Car(); 를 실행할 시 에러가 발생한다.
추상 클래스를 상속받은 파생 클래스가 추상 클래스의 순수 가상 함수를 구현하지 않았다면 파생 클래스도 추상 클래스가 된다. 이때는 파생 클래스 또한 인스턴스를 생성할 수 없다.
#include <iostream>
using namespace std;
class Car
{
public:
virtual ~Car() = default;
virtual void Accelerate(float power) = 0;
virtual void Brake(float power) = 0;
};
class Cart : public Car
{
public:
virtual ~Cart() {};
};
int main()
{
Cart *pCart = new Cart();
delete pCart;
return 0;
}
Cart *pCart = new Cart(); 코드 또한 추상 클래스라는 이유로 에러가 발생한다.
파생 클래스가 인스턴스를 생성하기 위해서는 추상클래스의 모든 순수 가상 함수를 구현해야 한다.
class Car
{
public:
virtual ~Car() = default;
virtual void Accelerate(float power) = 0;
virtual void Brake(float power) = 0;
};
class EngineCar : public Car
{
public:
EngineCar() {cout << "I have Engine" << endl;};
virtual ~EngineCar() {};
virtual void Accelerate(float power) override;
virtual void Brake(float power) override;
};
void EngineCar::Accelerate(float power)
{
cout << "Accelerating Power is " << power << endl;
}
void EngineCar::Brake(float power)
{
cout << "Braking Power is " << power << endl;
}
int main()
{
EngineCar *pEngineCar = new EngineCar();
pEngineCar->Accelerate(50);
pEngineCar->Brake(40);
delete pEngineCar;
return 0;
}
[예제 구현]
#include <iostream>
#include <vector>
using namespace std;
class Car
{
public:
virtual ~Car() = default;
virtual void Accelerate(float power) = 0;
virtual void Brake(float power) = 0;
};
class EngineCar : public Car
{
public:
EngineCar() {cout << "I have Engine" << endl;};
virtual ~EngineCar() {};
virtual void Accelerate(float power) override
{
cout << "Accelerating Power is " << power << endl;
cout << "Oil go, piston move, bomb, transmission, wheel rotating"<< endl;
};
virtual void Brake(float power) override
{
cout << "Braking Power is " << power << endl;
};
};
class ElectricCar : public Car
{
public:
ElectricCar() { cout <<"I have electric motor" << endl;}
virtual ~ElectricCar(){};
virtual void Accelerate(float power) override
{
cout << "Accelerating Power is " << power << endl;
cout << "Motor is Rotating" << endl;
};
virtual void Brake(float power) override
{
cout << "Braking Power is " << power << endl;
};
};
int main()
{
vector<Car*> CarVector;
CarVector.push_back(new EngineCar());
CarVector.push_back(new ElectricCar());
cout << endl;
for(int i=0; i<CarVector.size(); i++)
{
CarVector[i]->Accelerate(50);
CarVector[i]->Brake(40);
cout << endl;
}
return 0;
}
텍스트를 출력하는 것으로 대신했지만 위처럼 각 파생 클래스에 맞는 구현을 해주면 된다.
'프로그래밍' 카테고리의 다른 글
언리얼 엔진 4, 비쥬얼 스튜디오 2022 다운로드 및 설치 후 프로젝트 생성하기 (0) | 2023.02.09 |
---|---|
[STL] 컨테이너의 분류와 종류(순차 컨테이너, 연관 컨테이너, 비정렬 컨테이너) (0) | 2023.02.08 |
[C++] 레퍼런스(참조, &) 사용 시 주의 사항 (0) | 2023.02.08 |
[C++] decltype 이란? (0) | 2023.02.08 |
[C++] 함수 오버라이딩의 특수 케이스 확인하기 (0) | 2023.02.08 |