작년 9월에 C++에서 상속받을 수 없게 만든 class (=Java의 final class 류)에 관한 글을 소개했었다 – 상당히 불편한 방법으로 final 키워드 기능을 흉내낸다.
오늘 회사에서 (다른 팀이) 이전에 작성한 코드를 보다가, final keyword에 해당하는 MS VC++ 확장 키워드를 발견했다. MS VC++에서는 class의 상속에 관련되서 몇 가지 추가된 키워드를 가지고 있다.
- abstract – 추상 클래스를 선언할 수 있게 해주는 키워드. 이 클래스의 인스턴스;instance를 만들 수 없게 한다
- override – 특정 멤버 함수를 선언할 때 해당 선언이 기반;base 클래스의 멤버 함수를 override 한 것일 때만 유효하게 한다
- sealed – 특정 멤버 함수 혹은 클래스에 대해 해당 함수를 하위 클래스들이 override할 수 없게 막는다.
실제로 사용해보면 – 몇 가지 컴파일 오류를 섞어서 써보면 – 다음과 같은 코드가 나온다.
class Base abstract { public: virtual void foo() = 0; virtual void bar() = 0; }; // error - 'Base': a class declared as 'abstract' // cannot be instantiated Base b; class Derived : public Base { public: virtual void foo() override { }; virtual void bar() sealed override { }; // error - 'Derived::foo2' : method with override // specifier 'override' did not override // any base class methods virtual void foo2() override { }; }; class Derived2 sealed : public Derived { public: virtual void foo() override { }; // error - 'Derived::bar': function declared as 'sealed' // cannot be overridden by 'Derived2::bar' virtual void bar() override { }; }; // error - 'Derived3' : cannot inherit from 'Derived2' as it // has been declared as 'sealed' class Derived3 : public Derived2 { };
각각의 오류는 다음의 의미다. (순서대로)
- Base 는 abstract class라서 인스턴스를 만들지 못해서 변수 b의 정의는 에러가 된다.
- Derived에서는 상위 클래스에 없는 멤버 함수인 bar()를 덮어씌우려고;override해서 컴파일 에러를 보게 된다.
- 그리고 멤버 함수 레벨에서도 사용된 sealed로 인해 특정 가상 함수;virtual function이 더이상 덮어씌워지는 것을 막을 수 있다.
- 마지막으로 Derived3는 Derived2를 상속받으려 했으나 이 클래스 자체가 sealed라 상속받을 수 없다.
이런 형태로 추가적인 키워드 도입을 통해서 – 특히나 final에 해당하는 키워드는 너무 불편한 방법으로 우회해야 한다 – 좀 더 간편한 클래스 설계를 할 수 있게 된다. 그렇지만 호환성 문제는…
헉, 여태까지 C++/CLI에서만 쓰는 키워드인 줄 알았는데, 실험해보니 되네요. warning C4481 이 뜨긴 하지만요.
저도 다른 분 코드를 보다가 되나 싶어서 매뉴얼을 들여다봤는데 몇 개는 native C++ 에서도 되는 거더라구요;;
VC의 것은 VC에게, GCC의 것은 GCC에게[?]
안그래도 좋으니 양쪽 확장 서로 다 구현해놔! 가 내 생각이지만[…]
override, for each 애용 중;
override는 꽤 맘에 들었던 기능인데 (base의 signature가 바뀔 때 삽질할 확률 감소(…)), for each는 상당부분 C++ 자체 문법에서도 가능하다고 생각해서 사용을 자제(?) 중