java의 경우 기본이 virtual함수(메소드)지만 c++는 virtual이라는 키워드로 재정의 가능함에 대한 표시를 해준다.(+동적 바인딩을 통해 다형성을 구현)
바꾸어 말하면 virtual가 아닌 비가상 함수의 경우 절대로 재정의를 하지 말라는 뜻이 된다.
따라서 상속을 허용하는 Base class에서 비가상 함수가 존재한다면 이는 "파생에 관계없이 불변의 동작을 정의"함을 의미한다.
Base Class를 사용하는 사용자 입장에서는 절대로 비가상 함수에 대해서 재정의하려 들면 안된다.
그 이유는 비가상 함수는 가상 함수와 달리 정적으로 바인딩 되기 때문에 일관된 실행을 보장하지 못한다. 아래의 예를 통해 일관되지 못한 결과를 확인해보자.
Base가 되는 Car Class
class Car {
public:
Car(string model, string type)
: model(model),type(type)
{
}
virtual ~Car() {} //상속을 허용한다면 소멸자는 가상함수로 만들어야 한다.
void turnOnEngine() { //비가상 함수(불변의 동작)
cout<<"계기판 on"<<endl;
cout<<"라이트 on"<<endl;
cout<<"부릉!"<<endl;
}
virtual void run() = 0;
private:
string model;
string type;
};
Car Class를 상속받아서 비가상 함수를 재정의 하는 ElectronicCar Class
class ElectronicCar : public Car {
public:
ElectronicCar(string model, string type)
: Car(model, type)
{
}
void turnOnEngine() {
cout<<"부릉!"<<endl;
cout<<"라이트 on"<<endl;
cout<<"계기판 on"<<endl;
}
virtual void run() {
cout<<"전기로 주행"<<endl;
}
};
[실행 결과]
int main(void){
Car * pMyCar = new ElectronicCar("ModelX", "sedan");
pMyCar->turnOnEngine();
cout<<"===after cast==="<<endl;
ElectronicCar * pElectronicClar = dynamic_cast<ElectronicCar*>(pMyCar);
pElectronicClar->turnOnEngine();
return 0;
}
계기판 on 라이트 on 부릉! ===after cast=== 부릉! 라이트 on 계기판 on |
포인터 타입에 따라서 일관되지 못한 실행결과를 보여준다.
따라서 결론적으로 상속을 통한 구현을 할때 비가상 함수는 절대 건드리지 말자!(+ Base Class 설계자 입장에서는 비가상함수는 불변의 동작임을 인지하고 설계하자!)
'C and C++' 카테고리의 다른 글
[C++] Template 템플릿에 대한 기본적인 이해 (0) | 2018.09.09 |
---|---|
[C++] 상속에 대해 (0) | 2018.09.01 |
[C++] NVI (non-virtual interface) idiom 비가상 인터페이스에 대해 (0) | 2018.08.11 |
[C++] pImpl(pointer to implementation)를 이용해서 인터페이스와 구현을 분리하기 (0) | 2018.08.05 |
[C++] C++ 스타일의 캐스트 (0) | 2018.07.25 |