C++ - Virtual 전파

이 Box 클래스를 public 계승해도 안전할까?

class Box : public Object {
public:
    Box();
    ~Box();
};

정답은…이 정보만으로는 뭐라고 말할 수 없다 이다.
얼핏 보면 안 되다고 느끼는 사람도 많을 걸로 생각한다.특히 c++를 좀 알게 되었을 때의 사람은 우선 소멸자에 눈이 가지 않을까 생각한다.
소멸자 앞에 virtual이 붙지 않았다. virtual가 아닌 소멸자는 계승해서는 안 된다는 것은 c++에서 처음에 배우는 것중 하나이다.

좀 더 정보를 더해서, Object 클래스의 정의도 보자.

class Object {
public:
    virtual ~Object();
};

virtual이 정의된 소멸자가 정의되고 있다.
이것으로 분명하다. Box는 public 계승해도 안전하다.
“Box를 public계승해도 안전”이라는 표현을 더 자세히 쓰면 “Box를 계승한 클래슬 new로 생성하고, 그 포인터를 Box 혹은 Object의 포인터에 넣어 delete를 불러도 안전”이라는 것이다.

class MyBox:public Box{
public:
  MyBox();
  ~MyBox();
};
 
int main(){
   
  Box*box=new MyBox;
  delete box;//OK 문제 없이 MyBox의 소멸자를 호출
 
  Object obj=new MyBox;
  delete obj; //OK 이것도 문제 없이 MyBox의 소멸자를 호출
 
  return 0;
}

Object 클래스의 소멸자에 virtual이 붙어 있어 Object를 public 계승하는 모든 클래스의 소멸자는 암묵적으로 virtual 지정이 붙는다.
C++는 기저 클래스에서 virtual로 정의된 메서드는 파생처에서도 빠짐없이 virtual이 된다.

class Super{
public:
  Super(){}
  virtual~Super(){}
 
public:
  virtual void func1(){}
  virtual void func2(){};
  virtual void func3()=0;
};
 
class Sub:public Super{
public:
  Sub(){}
  ~Sub(){}//이는 암묵적으로 virtual
public:
  void func1(){};//이것도 암묵적으로 virtual
  void func2(){};//이것도 암묵적으로 virtual
  void func3(){};//이것도 암묵적으로 virtual
};

예전에는 파생처의 메소드에 virtual을 달아서 계승자의 메소드를 오버라이드 하고 있다는 것을 알려주는 방법이 있었지만 지금은 c++11에서 추가된 override 키워드가 있으니 이것을 사용한다.

class Super{
public:
  Super(){}
  virtual~Super(){}
 
public:
  virtual void func1(){}
  virtual void func2(){};
  virtual void func3()=0;
};
 
class Sub:public Super{
public:
  Sub(){}
  ~Sub(){}
public:
  void func1()override{}//오버라이드 한 것이 명쾌
  void func2()override{}//오버라이드 한 것이 명쾌
  void func3()override{}//오버라이드 한 것이 명쾌
  void func4()override{}//에러!기저 클래스에 없는 메소드 이름!
};

오버라이드 한다는 의사가 뚜렷해지면서 더 가독성이 높은 코드이다. 또 메소드 이름을 틀려도 지금까지는 잘못된 이름으로 새로운 메소드가 만들어져서 오류의 온상이 되었지만, override 키워드의 혜택으로 기저 클래스에 없는 메소드 이름을 지정한 경우 에러로 해준다.

정리

  1. 기저 클래스에서 virtual인 것은 파생처에서는 마음대로 virtual이 된다
  2. 오버라이드를 명시하기 위해서 c++11에서 추가된 override 키워드를 사용

이 글은 2020-07-27에 작성되었습니다.